Compaction

  • RocksDb的compaction,包含多种compaction Style, Compaction
  • Rocksdb默认采用Level-compaction
  • Manual-Compaction: 为什么需要manual,如何manual,以及影响manual的options设置。
  • compaction的相关option: rocksdb/options.h

Compaction触发时机

  • Options.disable_auto_compaction=true: 关闭rocksdb 的内置Compaction算法。

  • options.periodic_compaction_seconds=数值: 如果提供了CompactionFilter,通过设置非0数值,rocksdb会定期compaction对所有数据执行compactionFilter。执行完compactionFilter中被要求filter过滤掉的数据,会被标记为无效(delete marker),用户会查询不到,但是真正从磁盘上删除只有当发生compaction时才会被删除。如果不设置,默认30天,设置为0表示取消该功能。

  • 系统自动触发compaction: 比如level中文件数据量达到阈值。

  • 手动触发: 客户端主动调用DB::CompactRangeDB::CompactFiles方法会进行compaction. 阻塞时调用,直到compaction完成。nebula中ManualCompaction案例

  • Options::max_background_compactions: L1-LN, 在非0Level上多个compactions可以被并行执行, max_background_compactions控制了最大并行数量。

  • max_subcompactions大于1时,L0->L1, 我们会尝试把L0中数据文件分割开,用多线程合并到L1中。

RocksDb Level-compaction文件组织方式介绍

文件结构
  • rocksdb把磁盘上文件组织为多层,L0中数据是从memtable 中flush过来的
  • L0中的每个文件是有序的,但是非L0是整体有序的,即不仅每个文件有序而且文件之间也是有序的。
  • 非L0上的数据被分片保存在多个不同的sstable文件中
  • L0中key是有重复的,但是非L0中数据的key是没有重复的。所以在非L0中一个key只会包含在一个文件中。Lo中key可能包含在多个文件中。
  • 确定一个key的在该中的位置:先level中所有文件进行二分查找,找到那个file包含这个key,然后在这个file中再次二分查找,找到具体的位置。
  • 不同level中会包含相同的key, 高Level中的数据是旧的。


    非Lo是整体有序的

Compaction目的

  • 为了节省空间(删除无效的数据,合并文件数据,减少文件数量),和提高读性能(文件少了,检查key的效率就快了),会将磁盘上的sst文件定期进行合并compaction。
  • 每个非L0都有指定的文件总大小target_size,compaction就是要是每个非L0的大小维持在target_size之下,不同level的文件数量通常呈指数级增长。
image.png

Compaction

  • 当L0中文件到达level0_file_num_compaction_trigger时,L0中文件将会被Merged到L1中,因为L0中文件是有重叠的key,所以会将L0中所有文件都merge到L1中。


    image.png
  • L1中文件数量或者文件总size超过阈值后,会从L1中至少选择一个文件Merge到L2中key有交叠的文件中


    image.png

    image.png
  • 其它level同理,


    image.png
  • 如果需要,在非L0上多个compactions可以被并行执行, max_background_compactions控制了最大并行数量。

    image.png

  • 但是L0到L1的合并不可以并行操作,可能成为瓶颈,对于这种情况可以设置max_subcompactions大于1,这样,我们会尝试把数据文件分割开,用多线程去执行合并操作。

    L0到L1的多线程加速合并方式

定期compaction

  • 如果compaction filter存在的话,Rocksdb可以确保固定时间后数据都会经过compaction filter,这就是通过options.periodic_compaction_seconds参数控制,设置为0,则屏蔽该特性。如果使用默认值,rocksdb会将该值设置为30天。当进行compaction时,超过30天的数据都有资格去进行compaction(有些文件可能在compaction中会一直没有被选中),而且被compaction到原来的level中。
  • 如果没有compaction filter的compaction,其只会在合并过程中删除老的key,和保证level的文件大小,但是compaction filter的实现更多时为了根据业务逻辑实现对已有数据的删除/更新等操作。

参数设置

关于RocksDB层级关系中有几个相关的参数需要介绍:

参数 说明 默认值
write_buffer_size 限定Memtable的大小 64MB
level0_file_num_compaction_trigger 限定Level 0层的文件数量 4
target_file_size_base 每一层单个目标文件的大小 64MB
target_file_size_multiplier 每一层单个目标文件的乘法因子 1
max_bytes_for_level_base 每一层所有文件的大小 256MB
max_bytes_for_level_multiplier 每一层所有文件的乘法因子 10
level_compaction_dynamic_level_bytes 是否将Compact的策略改为层级从下往上应用 False
num_levels LSM的层级数量 7
  • 参数target_file_size_base和target_file_size_multiplier用来限定Compact之后的每一层的单个文件大小。target_file_size_base是Level-1中每个文件的大小,Level N层可以用target_file_size_base * target_file_size_multiplier ^ (L -1) 计算。target_file_size_base 默认为64MB,target_file_size_multiplier默认为1。

  • 参数max_bytes_for_level_base和max_bytes_for_level_multiplier用来限定每一层所有文件的限定大小。 max_bytes_for_level_base是Level-1层的所有文件的限定大小。Level N层的所有文件的限定大小可以用 (max_bytes_for_level_base) * (max_bytes_for_level_multiplier ^ (L-1))计算。max_bytes_for_level_base的默认为256MB,max_bytes_for_level_multiplier默认为10。

  • 参数level_compaction_dynamic_level_bytes用来指示Compact的策略改为层级从下往上应用。Target_Size(Ln-1) = Target_Size(Ln) / max_bytes_for_level_multiplier来限定大小:假如 max_bytes_for_level_base是 1GB, num_levels设为6。最底层的实际容量是276GB, 所以L1-L6层的大小分别是 0, 0, 0.276GB, 2.76GB, 27.6GB and 276GB。

更多参考 : RocksDB 的 Compact或官网

  • 如果多个level都可以compaction,那么优先选择哪个level?优先选择level中的哪个file?
    RocksDB会对每一层设置一个score,score用来表示进行Compact的优先级,score越大,越需要进行Compact。
  • compaction的参数:compaciton 发生的阈值?level的大小阈值等

如何Compact

Compact操作主要包括两种:将内存中的Immutable Memtable通过Flush转为磁盘上的SST文件,还有一种就是将磁盘上的SST文件,根据相关规则属性由上层向下层的转存。

Immutable Memtable的Flush

Flush的入口在db/db_impl_compaction_flush.ccBackgroundFlush()

当Memtable写满之后被转为Immutable Memtable,RocksDB会将其Flush至Level-0层:

  • 选择所有尚未被Flush的Immutable Memtable保存至mems_

  • 选择第一个Immutable Memtable即mems_[0]的version信息代表这次Flush操作的元信息

  • 调用WriteLevel0Table(),进行Level-0文件的写入

  • 将Memtable中的table_range_del_table_通过BuildTable构造新的SST文件,之后通过Add()插入数据

    • 这里的Table用的是Column Family的option默认设定的的BlockBasedTable,代码在table/block_based_table_builder.cc,通过Add()依次插入SST文件中的Index, Filter, Data各个Block,这部分涉及SST的文件布局,稍后的博文会着重介绍。
  • 将变化的SST文件元信息写入manifest文件

SST文件的Compact

Compact的入口在db/db_impl_compaction_flush.ccBackgroundCompaction(),我们这里依然以Leveled Compaction为例,Compaction的执行函数在CompactionJob::Run():

  • RocksDB会将所有的Level计算出score,经过冒泡排序,首先寻找score最高的Level,如果Level的score大于1,则选择这个Level进行Compaction
  • 选择Level-N中尚未被Compaction的文件PickCompaction()
  • 对于Level-0层文件,RocksDB总是选择所有的文件进行Compact执行操作,因为Level-0层的文件之间,可能会有key范围的重叠
  • 对于Level-N层,通过GetOverlappingInputs()选取Level-N+1中与Level-N中重叠的两部分SST文件
  • RocksDB的CompactionIterator::SeekToFirst()将这两部分文件里所有被删除的且不存在于更高层的Level的key重复的keyCompaction Filter中过滤的key标记为为无效
  • 将所有有效的key写入新的SST文件
  • 合并结束,利用VersionEdit更新VersionSet,更新统计信息
    u

你可能感兴趣的:(Compaction)