- RocksDb的compaction,包含多種compaction Style, Compaction
- Rocksdb默認(rèn)采用Level-compaction
- Manual-Compaction: 為什么需要manual,如何manual,以及影響manual的options設(shè)置。
- compaction的相關(guān)option: rocksdb/options.h
Compaction觸發(fā)時(shí)機(jī)
Options.disable_auto_compaction=true: 關(guān)閉rocksdb 的內(nèi)置Compaction算法。options.periodic_compaction_seconds=數(shù)值: 如果提供了CompactionFilter,通過設(shè)置非0數(shù)值,rocksdb會(huì)定期compaction對(duì)所有數(shù)據(jù)執(zhí)行compactionFilter。執(zhí)行完compactionFilter中被要求filter過濾掉的數(shù)據(jù),會(huì)被標(biāo)記為無效(delete marker),用戶會(huì)查詢不到,但是真正從磁盤上刪除只有當(dāng)發(fā)生compaction時(shí)才會(huì)被刪除。如果不設(shè)置,默認(rèn)30天,設(shè)置為0表示取消該功能。系統(tǒng)自動(dòng)觸發(fā)compaction: 比如level中文件數(shù)據(jù)量達(dá)到閾值。
手動(dòng)觸發(fā): 客戶端主動(dòng)調(diào)用
DB::CompactRange或DB::CompactFiles方法會(huì)進(jìn)行compaction. 阻塞時(shí)調(diào)用,直到compaction完成。nebula中ManualCompaction案例Options::max_background_compactions: L1-LN, 在非0Level上多個(gè)compactions可以被并行執(zhí)行, max_background_compactions控制了最大并行數(shù)量。max_subcompactions大于1時(shí),L0->L1, 我們會(huì)嘗試把L0中數(shù)據(jù)文件分割開,用多線程合并到L1中。
RocksDb Level-compaction文件組織方式介紹

- rocksdb把磁盤上文件組織為多層,L0中數(shù)據(jù)是從memtable 中flush過來的
- L0中的每個(gè)文件是有序的,但是非L0是整體有序的,即不僅每個(gè)文件有序而且文件之間也是有序的。
- 非L0上的數(shù)據(jù)被分片保存在多個(gè)不同的sstable文件中
- L0中key是有重復(fù)的,但是非L0中數(shù)據(jù)的key是沒有重復(fù)的。所以在非L0中一個(gè)key只會(huì)包含在一個(gè)文件中。Lo中key可能包含在多個(gè)文件中。
- 確定一個(gè)key的在該中的位置:先level中所有文件進(jìn)行二分查找,找到那個(gè)file包含這個(gè)key,然后在這個(gè)file中再次二分查找,找到具體的位置。
-
不同level中會(huì)包含相同的key, 高Level中的數(shù)據(jù)是舊的。
非Lo是整體有序的
Compaction目的
- 為了節(jié)省空間(刪除無效的數(shù)據(jù),合并文件數(shù)據(jù),減少文件數(shù)量),和提高讀性能(文件少了,檢查key的效率就快了),會(huì)將磁盤上的sst文件定期進(jìn)行合并compaction。
- 每個(gè)非L0都有指定的文件總大小target_size,compaction就是要是每個(gè)非L0的大小維持在target_size之下,不同level的文件數(shù)量通常呈指數(shù)級(jí)增長(zhǎng)。

Compaction
-
當(dāng)L0中文件到達(dá)level0_file_num_compaction_trigger時(shí),L0中文件將會(huì)被Merged到L1中,因?yàn)長(zhǎng)0中文件是有重疊的key,所以會(huì)將L0中所有文件都merge到L1中。
image.png -
L1中文件數(shù)量或者文件總size超過閾值后,會(huì)從L1中至少選擇一個(gè)文件Merge到L2中key有交疊的文件中
image.png
image.png -
其它level同理,
image.png -
如果需要,在非L0上多個(gè)compactions可以被并行執(zhí)行,
max_background_compactions控制了最大并行數(shù)量。
image.png -
但是L0到L1的合并不可以并行操作,可能成為瓶頸,對(duì)于這種情況可以設(shè)置
max_subcompactions大于1,這樣,我們會(huì)嘗試把數(shù)據(jù)文件分割開,用多線程去執(zhí)行合并操作。
L0到L1的多線程加速合并方式
定期compaction
- 如果compaction filter存在的話,Rocksdb可以確保固定時(shí)間后數(shù)據(jù)都會(huì)經(jīng)過compaction filter,這就是通過
options.periodic_compaction_seconds參數(shù)控制,設(shè)置為0,則屏蔽該特性。如果使用默認(rèn)值,rocksdb會(huì)將該值設(shè)置為30天。當(dāng)進(jìn)行compaction時(shí),超過30天的數(shù)據(jù)都有資格去進(jìn)行compaction(有些文件可能在compaction中會(huì)一直沒有被選中),而且被compaction到原來的level中。 - 如果沒有compaction filter的compaction,其只會(huì)在合并過程中刪除老的key,和保證level的文件大小,但是compaction filter的實(shí)現(xiàn)更多時(shí)為了根據(jù)業(yè)務(wù)邏輯實(shí)現(xiàn)對(duì)已有數(shù)據(jù)的刪除/更新等操作。
參數(shù)設(shè)置
關(guān)于RocksDB層級(jí)關(guān)系中有幾個(gè)相關(guān)的參數(shù)需要介紹:
| 參數(shù) | 說明 | 默認(rèn)值 |
|---|---|---|
| write_buffer_size | 限定Memtable的大小 | 64MB |
| level0_file_num_compaction_trigger | 限定Level 0層的文件數(shù)量 | 4 |
| target_file_size_base | 每一層單個(gè)目標(biāo)文件的大小 | 64MB |
| target_file_size_multiplier | 每一層單個(gè)目標(biāo)文件的乘法因子 | 1 |
| max_bytes_for_level_base | 每一層所有文件的大小 | 256MB |
| max_bytes_for_level_multiplier | 每一層所有文件的乘法因子 | 10 |
| level_compaction_dynamic_level_bytes | 是否將Compact的策略改為層級(jí)從下往上應(yīng)用 | False |
| num_levels | LSM的層級(jí)數(shù)量 | 7 |
參數(shù)target_file_size_base和target_file_size_multiplier用來限定Compact之后的每一層的單個(gè)文件大小。target_file_size_base是Level-1中每個(gè)文件的大小,Level N層可以用target_file_size_base * target_file_size_multiplier ^ (L -1) 計(jì)算。target_file_size_base 默認(rèn)為64MB,target_file_size_multiplier默認(rèn)為1。
參數(shù)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))計(jì)算。max_bytes_for_level_base的默認(rèn)為256MB,max_bytes_for_level_multiplier默認(rèn)為10。
參數(shù)level_compaction_dynamic_level_bytes用來指示Compact的策略改為層級(jí)從下往上應(yīng)用。Target_Size(Ln-1) = Target_Size(Ln) / max_bytes_for_level_multiplier來限定大?。杭偃?max_bytes_for_level_base是 1GB, num_levels設(shè)為6。最底層的實(shí)際容量是276GB, 所以L1-L6層的大小分別是 0, 0, 0.276GB, 2.76GB, 27.6GB and 276GB。
更多參考 : RocksDB 的 Compact或官網(wǎng)
- 如果多個(gè)level都可以compaction,那么優(yōu)先選擇哪個(gè)level??jī)?yōu)先選擇level中的哪個(gè)file?
RocksDB會(huì)對(duì)每一層設(shè)置一個(gè)score,score用來表示進(jìn)行Compact的優(yōu)先級(jí),score越大,越需要進(jìn)行Compact。 - compaction的參數(shù):compaciton 發(fā)生的閾值?level的大小閾值等
如何Compact
Compact操作主要包括兩種:將內(nèi)存中的Immutable Memtable通過Flush轉(zhuǎn)為磁盤上的SST文件,還有一種就是將磁盤上的SST文件,根據(jù)相關(guān)規(guī)則屬性由上層向下層的轉(zhuǎn)存。
Immutable Memtable的Flush
Flush的入口在db/db_impl_compaction_flush.cc的BackgroundFlush()
當(dāng)Memtable寫滿之后被轉(zhuǎn)為Immutable Memtable,RocksDB會(huì)將其Flush至Level-0層:
選擇所有尚未被Flush的Immutable Memtable保存至
mems_選擇第一個(gè)Immutable Memtable即
mems_[0]的version信息代表這次Flush操作的元信息調(diào)用
WriteLevel0Table(),進(jìn)行Level-0文件的寫入-
將Memtable中的
table_和range_del_table_通過BuildTable構(gòu)造新的SST文件,之后通過Add()插入數(shù)據(jù)- 這里的
Table用的是Column Family的option默認(rèn)設(shè)定的的BlockBasedTable,代碼在table/block_based_table_builder.cc,通過Add()依次插入SST文件中的Index, Filter, Data各個(gè)Block,這部分涉及SST的文件布局,稍后的博文會(huì)著重介紹。
- 這里的
將變化的SST文件元信息寫入manifest文件
SST文件的Compact
Compact的入口在db/db_impl_compaction_flush.cc的BackgroundCompaction(),我們這里依然以Leveled Compaction為例,Compaction的執(zhí)行函數(shù)在CompactionJob::Run():
- RocksDB會(huì)將所有的Level計(jì)算出score,經(jīng)過冒泡排序,首先尋找score最高的Level,如果Level的score大于1,則選擇這個(gè)Level進(jìn)行Compaction
- 選擇Level-N中尚未被Compaction的文件
PickCompaction() - 對(duì)于Level-0層文件,RocksDB總是選擇所有的文件進(jìn)行Compact執(zhí)行操作,因?yàn)長(zhǎng)evel-0層的文件之間,可能會(huì)有key范圍的重疊
- 對(duì)于Level-N層,通過
GetOverlappingInputs()選取Level-N+1中與Level-N中重疊的兩部分SST文件 - RocksDB的
CompactionIterator::SeekToFirst()將這兩部分文件里所有被刪除的且不存在于更高層的Level的key、重復(fù)的key、Compaction Filter中過濾的key標(biāo)記為為無效 - 將所有有效的key寫入新的SST文件
- 合并結(jié)束,利用VersionEdit更新VersionSet,更新統(tǒng)計(jì)信息
u






