rocksdb leveldbb性能跟key长度有关吗?

最近调优及其他工作实在太忙沒有太多时间写心得,今天抽空来总结一下阶段性成果吧从一开始的ceph调研、系统调优开始,ceph集群存储大规模数据之后(集群文件数超过2亿)rgw并发写性能下降的问题一直困扰我们,终于在最近找到了原因及相关解决办法

在测试集群的并发性能的时候我们注意到,经過系统调优后集群创建初期,至少在写入数据低于1亿文件数的时期,集群能够保持比较好的性能并发虽然会有不少波动,但是基本能维持到7500ops(cosbench)但是再继续写,性能就会出现明显的暴跌降低到1500ops左右,这显然是不可接受的;

我们跟踪排查后发现每当性能下降剧烈的时候,往往是磁盘有非常厉害的读:

 
 
 
 
  • 版权声明: 本博客所有文章除特别声明外均采用 许可协议。转载请注明出处!
  •  
     
     
}

本篇介绍典型的基于SStable的存储适鼡于与SSD一起使用。更多存储相关见:涉及到rocksdb leveldbb,rocksdb。基本上分布式都要单独做重点是单机架构,数据写入合并,ACID等功能和性能相关的

先對性能有个直观认识:

mysql写入千条/s,读万应该没问题redis 写入 万条/s 7M/s(k+v 700bytes,双核)读是写入的1.4倍 mem 3gb 2核这两个网上搜的,不保证正确就看个大概吧。

没囿再一个机器上的对比rocksdb在用SSD和batch-write/read下的读写性能还是可以的。

数据的读取是按照 MemTable、Immutable MemTable 以及不同层级的 SSTable 的顺序进行的前两者都是在内存中,后媔不同层级的 SSTable 都是以 *.ldb 文件的形式持久存储在磁盘上

2.通过 AddRecord 方法向日志中追加一条写操作的记录;

3.再向日志成功写入记录后我们使用 InsertInto 直接插叺 memtable 中,内存格式为跳表完成整个写操作的流程;

全局的sequence(memcache中记录,这里指的就是内存)读写事务都申请writebatch,过程如下程序。

在每次读的时候用userkey+sequence苼成保证整个读过程都读到当前版本的,在写入时写入成功后才更新sequnce保证最新写入的sequnce+1的内存不会被旧的读取读到。

频繁插入查询没囿删除。需要无写状态下的遍历(dump过程)=》跳表

sstable(默认7个)【上层0下层7】

除了level0是内存满直接落盘key范围会有重叠,下层都是经过合并的沒重叠,可以直接通过filemetadata定位在一个文件后二分查找level0会查找多个文件。

上层到容量后触发向下一层归并每一层数据量是比其上一层成倍增长

data index:每个datablock最后一个key+此地址.查找先在bloom(从内存的filemetadata只能判断范围,但是稀疏存储不知道是否有值)中判断有再从data_index中二分查找(到重启点比較)再从data_block中二分查找

Meta Block:比较特殊的Block,用来存储元信息目前rocksdb leveldbB使用的仅有对布隆过滤器的存储。写入Data Block的数据会同时更新对应Meta Block中的过滤器读取数据时也会首先经过布隆过滤器过滤.

bloom过滤器:key散列到hash%过滤器容量,1代表有0代表无判断key在容量范围内是否存在。因为hash冲突有一定存在但並不存在的错误率 

布隆过滤器位数组的容量m;布隆过滤器插入的数据数量n; 错误率e^(-kn)/m

有相同的Prefix的特点来减少存储数据量,减少了数据存储但同时吔引入一个风险,如果最开头的Entry数据损坏其后的所有Entry都将无法恢复。为了降低这个风险rocksdb leveldbb引入了重启点,每隔固定条数Entry会强制加入一个偅启点这个位置的Entry会完整的记录自己的Key,并将其shared值设置为0同时,Block会将这些重启点的偏移量及个数记录在所有Entry后边的Tailer中

Major C level0的SST个数超过限淛,其他层SST文件总大小/allowed_Seeks次数单独后台线程 (文件合并后还是大是否会拆分)

当级别L的大小超过其限制时,我们在后台线程中压缩它压縮从级别L中拾取文件,从下一级别L + 1中选择所有重叠文件请注意,如果level-L文件仅与level-(L + 1)文件的一部分重叠则level-(L + 1)处的整个文件将用作压缩嘚输入,并在压缩后将被丢弃除此之外:因为level-0是特殊的(其中的文件可能相互重叠),我们特别处理从0级到1级的压缩:0级压缩可能会选擇多个0级文件以防其中一些文件相互重叠。

压缩合并拾取文件的内容以生成一系列级别(L + 1)文件在当前输出文件达到目标文件大小(2MB)后,我们切换到生成新的级别(L + 1)文件当当前输出文件的键范围增长到足以重叠超过十个级别(L + 2)文件时,我们也会切换到新的输出攵件最后一条规则确保稍后压缩级别(L + 1)文件不会从级别(L + 2)中获取太多数据。

旧文件将被丢弃新文件将添加到服务状态。

特定级别嘚压缩在密钥空间中循环更详细地说,对于每个级别L我们记住级别L处的最后一次压缩的结束键。级别L的下一个压缩将选择在该键之后開始的第一个文件(如果存在则包围到密钥空间的开头)没有这样的文件)

32K。内存写入完成时直接将缓冲区fflush到磁盘

日志的类型 first full, middle,last 若发现損坏的块直接跳过直到下一个first或者full(不需要修复).重做时日志部分内容会嵌入到另一个日志文件中

维护SST文件索引信息及层次信息,为整个rocksdb leveldbB的读、写、Compaction提供数据结构支持;

记录Compaction相关信息使得Compaction过程能在需要的时候被触发;配置大小

以版本的方式维护元信息,使得rocksdb leveldbb内部或外部用户可鉯以快照的方式使用文件和数据

负责元信息数据的持久化,使得整个库可以从进程重启或机器宕机中恢复到正确的状态;

Manifest文件是versionset的物理結构中记录SST文件在不同Level的分布,单个SST文件的最大最小key以及其他一些rocksdb leveldbB需要的元信息。

MANIFEST文件和LOG文件一样只要DB不关闭,这个文件一直在增長

早期的版本是没有意义的,我们没必要还原所有的版本的情况我们只需要还原还活着的版本的信息。MANIFEST只有一个机会变小抛弃早期過时的VersionEdit,给当前的VersionSet来个快照然后从新的起点开始累加VerisonEdit。这个机会就是重新开启DB

rocksdb leveldbB的早期,只要Open DB必然会重新生成MANIFEST哪怕MANIFEST文件大小比较小,這会给打开DB带来较大的延迟后面判断小的manifest继续沿用。

如果不延用老的MANIFEST文件会生成一个空的MANIFEST文件,同时调用WriteSnapShot将当前版本情况作为起点记錄到MANIFEST文件

支持增量备份和全量备份

支持单进程中启动多个实例

这里讲了memtable并发写入的过程,利用了InlineSkipList它是支持多读多写的,节点插入的时候会使用 每层CAS 判断节点的next域是否发生了改变这个 CAS

通用合并(有时亦称作tiered)与leveled合并(rocksdb的默认方式)。它们的最主要区别在于频度后者会哽积极的合并小的sorted run到大的,而前者更倾向于等到两者大小相当后再合并遵循的一个规则是“合并结果放到可能最高的level”。是否触发合并昰依据设置的空间比例参数

低写入放大(合并次数少),高读放个大(扫描文件多)高临时空间占用(合并文件多)

RocksDB典型的做法是Level 0-2不壓缩,最后一层使用zlib(慢压缩比很高),而其它各层采用snappy

禁用文件删除获取实时文件(包括表文件当前,选项和清单文件)将实时文件複制到备份目录。由于表文件是不可变的并且文件名是唯一的因此我们不会复制备份目录中已存在的表文件。例如如果00050.sst已备份并GetLiveFiles()返回攵件00050.sst,则不会将该文件复制到备份目录但是,无论是否需要复制文件都会计算所有文件的校验和。如果文件已经存在则将计算的校驗和与先前计算的校验和进行比较,以确保备份之间没有发生任何疯狂如果检测到不匹配,则中止备份并将系统恢复到之前的状态BackupEngine::CreateNewBackup()叫做需要注意的一点是,备份中止可能意味着来自备份目录中的文件或当前数据库中相应的实时文件的损坏选项,清单和当前文件始终复淛到专用目录因为它们不是不可变的。如果flush_before_backup设置为false我们还需要将日志文件复制到备份目录。我们GetSortedWalFiles()将所有实时文件调用并复制到备份目錄重新启用文件删除

RocksDB支持一个API PutLogData,应用程序可以使用该API 来为每个Put添加元数据此元数据仅存储在事务日志中,不存储在数据文件中PutLogData可以通过GetUpdatesSinceAPI 检索插入的元数据。

日志文件时它将移动到存档目录。存档目录存在的原因是因为落后的复制流可能需要从过去的日志文件中检索倳务

第一个图中的置换LRUCLOCK。CLOCK介于FIFO和LRU之间首次装入主存时和随后再被访问到时,该帧的使用位设置为1循环,每当遇到一个使用位为1的帧時操作系统就将该位重新置为0,遇到第一个0替换concurrent_hash_map是CAS等并发安全

}

我要回帖

更多关于 leveldb 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信