- 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::CompactRange
或DB::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中的数据是旧的。
Compaction目的
- 为了节省空间(删除无效的数据,合并文件数据,减少文件数量),和提高读性能(文件少了,检查key的效率就快了),会将磁盘上的sst文件定期进行合并compaction。
- 每个非L0都有指定的文件总大小target_size,compaction就是要是每个非L0的大小维持在target_size之下,不同level的文件数量通常呈指数级增长。
Compaction
-
当L0中文件到达level0_file_num_compaction_trigger时,L0中文件将会被Merged到L1中,因为L0中文件是有重叠的key,所以会将L0中所有文件都merge到L1中。
-
L1中文件数量或者文件总size超过阈值后,会从L1中至少选择一个文件Merge到L2中key有交叠的文件中
-
其它level同理,
-
如果需要,在非L0上多个compactions可以被并行执行,
max_background_compactions
控制了最大并行数量。
-
但是L0到L1的合并不可以并行操作,可能成为瓶颈,对于这种情况可以设置
max_subcompactions
大于1,这样,我们会尝试把数据文件分割开,用多线程去执行合并操作。
定期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.cc
的BackgroundFlush()
当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.cc
的BackgroundCompaction()
,我们这里依然以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、重复的key、Compaction Filter中过滤的key标记为为无效 - 将所有有效的key写入新的SST文件
- 合并结束,利用VersionEdit更新VersionSet,更新统计信息
u