LevelDB 代码撸起来!

  • 时间:
  • 浏览:2

这须要改善读性能就都须要借助块缓存了

既然同一二个多 Key 存在多个版本的数据,对于同一二个多 Key,遍历操作是何如正确处理重复的呢?关于一点 什么的大问题大伙儿儿后续再深入探讨。

将一点 目录里边的文件完整删掉,一点 库就彻底清空了。

布隆过滤器在显著提升性能的共同,也是须要浪费一定的磁盘空间。LevelDB 须要将布隆过滤器的二进制数据存储到数据块中,不过布隆过滤器的空间占比相对而言有无很高,完整在可接受范围之内。

一点 例子中大伙儿儿将写入更多的数据 —— 50w 条,当数据量增多时,LevelDB 将形成更深的层级。共同为了构发名人读 miss 的效果,大伙儿儿写入偶数的键值对,但会 再随机读取奇数的键值对。再对比增加布隆过滤器前后的读性能差异。

压缩

LevelDB 的压缩算法采用 Snappy,一点 算法解压缩数率很高,在压缩比相差不大的状况下 CPU 消耗很低。官方不建议关闭压缩算法,不过经过我的测试发现,关闭压缩嘴笨 都须要显著提升读性能。不过关闭了压缩,这也原困你的磁盘空间要浪费好几倍,这代价全都低。

运行时间变成了只有 5s。再将 N 改成 10,运行时间变成了只有 12s。即使是 12s,写的平均 QPS 也高达 8w/s,这还是很客观的。

Maven 依赖

下载下面的依赖包地址,你就都须要得到一二个多 支持全平台的 jar 包。LevelDB 在不同的操作系统平台会编译出不同的动态链接库形式,一点 jar 包将所有平台的动态链接库都包含进来了。

为何会 会 说批量写都须要保证内内外部一系列操作的原子性呢,全都可能性一点 互斥锁的保护让写操作单守护进程化了。可能性一点 粗粒度锁的存在,LevelDB 写操作的性能被大大限制了。这也成了并且居上的 RocksDB 重点优化的方向。

运行了共要 1s 多点时间,完毕后大伙儿儿看后目录中 sst 文件那末 了

读性能

可能性大伙儿儿将里边的代码加上时间打点,观察读写性能差异,让我发现一二个多 有趣的什么的大问题,那全都写性能比读性能须要好,嘴笨 本例中所有的读操作有无命中的。

很明显,每一二个多 普通写操作最终有无被转加上一二个多 批量写操作,只不过 N=1 。这正好解释了为何会 会 当 N=1 时批量写操作和普通写操作相差无几。

前后两次遍历从快照中获取到的数据还是一致的,也全都说里边的写操作根本那末 影响到快照的状况,这全都大伙儿儿无需的结果。那快照的原理是什么呢?

LevelDB 的大致原理可能性讲完了,本节大伙儿儿要亲自使用 Java 语言第三方库 leveldbjni 来实践一下 LevelDB 的各种底部形态。一点 库使用了 Java Native Interface 计数将 C++ 实现的 LevelDB 包装成了 Java 平台 的 API。其它语言同样也是采用了这类 JNI 的技术来包装的 LevelDB。

大伙儿儿再继续追踪 WriteBatch 的源码我发现每一二个多 批量写操作都须要使用互斥锁。当批次 N 值比较大时,共要加锁的平均次数减少了,于是整体性能就提升了。但会 全都会提升很多,可能性加锁两种的损耗占比开销全都是很重大。这也原困在守护进程场合,写操作性能会下降,可能性锁之间的竞争将原困内耗增加。

结论是在读 miss 开启了布隆过滤器场景下块缓存几乎不起作用。但会 这并有无说块缓存那末 用,在读命中的状况下,块缓存的作用还是很大的。

同步 vs 异步

上一节大伙儿儿提到 LevelDB 还提供了同步写的 API,确保操作日志落地后才 put 辦法 才返回。它的性能会明显弱于普通写操作,下面大伙儿儿来对比一下两者的性能差异。

快照和遍历

LevelDB 提供了快照读功能都须要保证同一二个多 快照内同一二个多 Key 读到的数据保持一致,正确处理「不可重复读」的存在。下面大伙儿儿使用快照来尝试一下遍历操作,在遍历的过程中顺便还修改对应 Key 的值,看看快照读是有无都须要隔离写操作。

快照的原理嘴笨 非常简单,简单到让我怀疑人生。对于库中的每一二个多 键值对,它会可能性修改操作而存在多个值的版本。在数据库文件内容合并前一天 ,同一二个多 Key 可能性会存在于多个文件中,每个文件中的值版本不一样。一点 版本号是由数据库唯一的全局自增计数值标记的。快照会记录当前的计数值,在当前快照里读取的数据都须要和快照的计数值比对,只有小于一点 计数值才是有效的数据版本。

大伙儿儿再观察数据库的目录中,LevelDB 都创建了什么东西

可能性大伙儿儿那末 手工调用数据下发 API,LevelDB 内内外部有无一定的策略来定期下发。

普通写 VS 批量写

LevelDB 提供了批量写操作,它会无需这类于 Redis 的管道都须要加快指令的运行呢,下面大伙儿儿来尝试使用 WriteBatch,对比一下普通的写操作,看看性能差距有多大。

那为何会 会 批量写还是会比普通写调快呢?要回答一点 什么的大问题就须要追踪 LevelDB 的源码,还在这累积逻辑比较简单,大伙儿儿应该都都须要理解,全都这里就直接贴出来了。

数据合并

LevelDB 提供了数据合并的手动调用 API,下面大伙儿儿手动下发一下,看看下发有无存在什么

原文发布时间为:2019-1-8

本文作者:逅弈

本文来自云栖社区合作协议协议伙伴“ 码洞 ”,了解相关信息都须要关注“ codehole”微信公众号

下面大伙儿儿将读操作改成随机读,就会发现读写性能存在很大的差别

一点 目录里大伙儿儿看后了有全都 sst 扩展名的文件,它全都 LevelDB 的磁盘数据文件,一点 LevelDB 的版本数据文件的扩展名是 ldb,不过内内外部格式一样,仅仅是扩展名不同而已。其中还有一二个多 log 扩展名的文件,这全都操作日志文件,记录了最近一段时间的操作日志。其它几次大些名称文件大伙儿儿先很多去了解,后续大伙儿儿再仔细解释。

这是可能性写操作记完操作日志将数据写进内存后就返回了,而读操作有可能性内存读 miss,但会 要去磁盘读。难能可贵读写性能差距有无非常明显,是可能性 LevelDB 会缓存最近一次读取的数据块,但会 我的我每人及电脑的磁盘是 SSD 磁盘,读性能都好。可能性是普通磁盘,就会看出明显的性能差异了。

下面大伙儿儿再打开压缩,对比一下结果,读性能差距接近 1 倍

增查删 API

一点 例子中大伙儿儿将自动创建一二个多 LevelDB 数据库,但会 往里边塞入 50w 条数据,再取出来,再删掉所有数据。一点 例子在我的 Mac 上会运行了共要 10s 的时间。也全都说读写平均 QPS 高达 50w/s,完整都须要媲美 Redis,不过这共要也是可能性键值对都比较小,在实际生产环境中性能应该那末 那末 高,它共要应该比 Redis 稍慢一点。

待 LevelDB 在两种条件下触发数据合并(compact)时才会真的删除相应的键值对。

一点 API 都须要挑选范围进行下发,可能性 begin 参数为 null,那就表示从头开始英文了,可能性 end 参数为 null,那就表示一个劲到尾部。

里边一点 同步写操作足足花了 90s 多的时间。将 sync 选项加上后,只须要 3s 多点。性能差距高达 50 倍。下面大伙儿儿来简单改造一下里边的代码,让它变成间隔同步写,也全都每隔 N 个写操作同步一次,取 N = 50。

我知道你让我想到里边的例子中有无所有的数据最终都被删除了么,为何会 有无有很多的 sst 数据文件呢?这是可能性 LevelDB 的删除操作并有无真的立即删除键值对,全都将删除操作转加上了更新操作写进去了一二个多 特殊的键值对,一点 键值对的值累积是一二个多 特殊的删除标记。

都须要明显看出,读性能提升了 3 倍,这是一二个多 非常了不起的性能提升。在读 miss 开启了布隆过滤器的状况下,大伙儿儿再试试打开块缓存,看看是有无还能再继续提升读性能

再加上注释,打开布隆过滤器,观察结果

将批次数量 N 分别改成 10、50、50,运行后都须要发现耗时差很多,共也不有无 2s 多点。这原困批量写很多会大幅提升写操作的吞吐量。但会 将 N 改成 1 后让我发现耗时和普通写操作相差无几,共也不 3s 多,再将 N 改成 2、5 等,耗时还是会有所降低,到 2s 多 左右就稳定了,此时提升 N 值不再有明显效果。这原困批量写操作嘴笨 会比普通写调快,但会 相差全都会过大。它不同于 Redis 的管道都须要大幅减少网络开销带来的明显性能提升,LevelDB 是纯内存数据库,根本谈不上网络开销。

布隆过滤器

leveldbjni 那末 封装 LevelDB 提供的布隆过滤器功能。全都为了尝试布隆过滤器的效果,大伙儿儿须要试试其它语言,这里我使用 Go 语言的 levigo 库。