| 導(dǎo)語 對于 LevelCompact 策略,RocksDB會根據(jù)每一層不同的策略計(jì)算出CompactScore,根據(jù)CompactScore大小來決定那一層將會優(yōu)先進(jìn)行Compact,然后選擇Level-N 和Level-(N+1)的文件進(jìn)行Compact。如何計(jì)算CompactScore? 如何選擇文件進(jìn)行Compact?Compact有哪些參數(shù)?如何知道RocksDB當(dāng)前的一個(gè)狀態(tài)?

 

RocksDB是基于LSM結(jié)構(gòu)的K-V存儲引擎,由于數(shù)據(jù)文件采用Append Only方式寫入,而對于過期的數(shù)據(jù)、重復(fù)的數(shù)據(jù)必然會存在有多份副本,這部分?jǐn)?shù)據(jù)通過Compact的方式進(jìn)行逐步的清理。 
那么這里好奇的提出幾個(gè)問題,由這幾個(gè)問題引出下文:

  1. RocksDB是如何進(jìn)行Compact 的?

  2. Compact的時(shí)候這些文件是如何進(jìn)行選擇的?

  3. Compact在什么時(shí)候、或者什么條件下觸發(fā)?

  4. 對于Compact我們能知道哪些信息?通過TRedis怎么查看這部分信息?

  5. 有哪些參數(shù)可以控制或者影響到Compact

由于我們的TRedis底層采用RocksDB存儲引擎進(jìn)行持久化,底層數(shù)據(jù)文件采用分層的方式管理,故這里討論的Compact 基于Level Compact 。

數(shù)據(jù)怎么來?我們調(diào)用TRedis接口進(jìn)行寫數(shù)據(jù)時(shí),數(shù)據(jù)會先寫入到內(nèi)存中的Memtable里邊,當(dāng)Memtable寫滿后會寫入下一個(gè)Memtable,Memtable采用Skiplist結(jié)構(gòu)以此保證數(shù)據(jù)按照Key的字典序進(jìn)行排序,同時(shí)這個(gè)Memtable會被后臺線程刷到磁盤文件–Level-0,當(dāng)Level-0文件個(gè)數(shù)達(dá)到一定數(shù)量,Compact線程可能會進(jìn)行Compact,由此產(chǎn)生Level-1,當(dāng)Level-1文件總大小達(dá)到一定大小后, Compact線程可能會進(jìn)行Compact,由此產(chǎn)生Level-2,…….

RocksDB對每一層的處理規(guī)則不太一樣,由于Level-0層的數(shù)據(jù)直接由Memtable dump得到,從而不能保證Level-0層的每個(gè)文件Key的范圍不能有交集,故對Level-0層的會進(jìn)行特殊處理,而對于Level-1+層處理規(guī)則一樣。

Level-0 層的文件在不停的從Memtable 中dump出來,那么何時(shí)才會把這些Level-0層的文件合并到Level-1 ? 
RocksDB對對每一層進(jìn)行打分,分?jǐn)?shù)從0~1000000,這個(gè)分?jǐn)?shù)的大小決定了進(jìn)行Compact 的優(yōu)先級,分?jǐn)?shù)越大,越先進(jìn)行Compact。

那么這個(gè)分?jǐn)?shù)如何計(jì)算出來?
  • 如果是Level-0層,會先算出當(dāng)前有多少個(gè)沒有進(jìn)行Compact 的文件個(gè)數(shù)numfiles, 然后根據(jù)這個(gè)文件的個(gè)數(shù)進(jìn)行判斷,當(dāng)numfiles<20 時(shí),Score = numfiles/4;當(dāng)24>numfiles>=20時(shí),Score = 10000;當(dāng) numfiles>=24時(shí),Score = 1000000

相關(guān)參數(shù)說明
level0_file_num_compaction_trigger4當(dāng)有4個(gè)未進(jìn)行Compact的文件時(shí),達(dá)到觸發(fā)Compact的條件
level0_slowdown_writes_trigger20當(dāng)有20個(gè)未進(jìn)行Compact的文件時(shí),觸發(fā)RocksDB,減慢寫入速度
level0_stop_writes_trigger24當(dāng)有24個(gè)未進(jìn)行Compact的文件時(shí),觸發(fā)RocksDB停止寫入文件,此時(shí)會盡快的Compact Level-0層文件
  • 如果是Level-1+層,會去計(jì)算每一層未進(jìn)行Compact文件的總Size,然后再和這一層的”容量值”做對比,得到一個(gè)比值,這個(gè)值就是該層的 CompactScore ,也就是說對于Level-1+層,Compact 觸發(fā)條件是看這一層文件的大小而不是個(gè)數(shù)。Score = level_bytes / MaxBytesForLevel(level)

對于Level-1+層,每一層的最大Bytes 是如何計(jì)算出來的?

Level-1 層 文件總大小由 max_bytes_for_level_base 參數(shù)控制,而 Level-2 層的大小通過: Level_max_bytes[N] = Level_max_bytes[N-1] * max_bytes_for_level_multiplier^(N-1)*max_bytes_for_level_multiplier_additional[N-1] 計(jì)算得出:

參數(shù)說明
max_bytes_for_level_base10485760用于指定Level-1 層總大小,超過這個(gè)值滿足觸發(fā)Compact條件
max_bytes_for_level_multiplier10每一層最大Bytes 乘法因子
max_bytes_for_level_multiplier_addtl[2]1Level-2 層總大小調(diào)整參數(shù)
max_bytes_for_level_multiplier_addtl[3]1Level-3 層總大小調(diào)整參數(shù)
max_bytes_for_level_multiplier_addtl[4]1Level-4 層總大小調(diào)整參數(shù)
max_bytes_for_level_multiplier_addtl[5]1Level-5 層總大小調(diào)整參數(shù)
max_bytes_for_level_multiplier_addtl[6]1Level-6 層總大小調(diào)整參數(shù)
if (i > 1) {
     level_max_bytes[i] = MultiplyCheckOverflow(
         MultiplyCheckOverflow(level_max_bytes[i - 1],
                               max_bytes_for_level_multiplier),
         max_bytes_for_level_multiplier_additional[i - 1]);
   } else {
     level_max_bytes[i] = max_bytes_for_level_base;
   }
在進(jìn)行Compact的時(shí)候,會選擇哪些文件進(jìn)行Compact操作呢?

對于Level-0層文件,RocksDB總是選擇所有的文件進(jìn)行Compact操作,因?yàn)長evel-0層的文件之間,可能會有key范圍的重疊。 
對于Level-N (N>1)層的文件,會先按照文件大小排序(冒泡排序),選出最大的文件,并計(jì)算這個(gè)文件Key 的起止范圍,通過這個(gè)范圍查找Level-N+1層文件,把選出的Level-N 文件和Level-N+1 文件做為輸入,并且在Level-N+1新建一個(gè)或多個(gè)SST文件作為輸出。 
可以通過設(shè)置max_background_compactions 大于1 來使用并行Compact,不過這個(gè)并行Compact 不能作用到Level-0層。

  // Find the compactions by size on all levels.
for (int i = 0; i < NumberLevels() - 1; i++) {
   double score = vstorage->CompactionScore(i);
   level = vstorage->CompactionScoreLevel(i);
assert(i == 0 || score <= vstorage->CompactionScore(i - 1));
if ((score >= 1)) {
c = PickCompactionBySize(mutable_cf_options, vstorage, level, score);
if (c == nullptr ||
ExpandWhileOverlapping(cf_name, vstorage, c) == false) {
       delete c;
c = nullptr;
     } else {
break;
     }
   }
 }

如何查看RocksDB內(nèi)部狀態(tài)?

一般情況下內(nèi)部狀態(tài)會定時(shí)dump出來存放到LOG文件里,這個(gè)時(shí)間可以通過:stats_dump_period_sec 來控制這個(gè)dump內(nèi)部狀態(tài)的頻率,如果是TRedis V1.2.9 版本以上可以通過 rocksprop rocksdb.cfstats 得到這些信息:

關(guān)于這些參數(shù)的解釋如下:

列名解釋
LevelLevel0~N、或者合計(jì)值、或者Int
FilesSST文件數(shù)量/待進(jìn)行compact的SST文件數(shù)
Size(MB)SST文件總大小
Score Read(GB)代表進(jìn)行compact的優(yōu)先級,分?jǐn)?shù)越高越會優(yōu)先進(jìn)行compact
Rn(GB)進(jìn)行compact時(shí),讀當(dāng)前層文件的大小
Rnp1(GB)進(jìn)行compact時(shí),讀取下一層文件的大小
Write(GB)compact完成時(shí),寫入文件的大小
Wnew(GB)新產(chǎn)生的數(shù)據(jù)大?。?寫入到Level-(N+1)層的大小 - 從Level-(N+1)層讀的大小
RW-Amp讀寫放大比例 : 總的讀寫 / 從Level-N層讀的大小
W-Amp寫放大比例: 寫入Level-(N+1)層大小/從Level-N層的大小
Rd(MB/s)讀文件速度: (bytes_readn + bytes_readnp1 )/((micros + 1) / 1000000.0)
Wr(MB/s)寫文件速度: bytes_written / ((micros + 1) / 1000000.0)
Rn(cnt)Files read from level N during compaction between levels N and N+1
Rnp1(cnt)Files read from level N+1 during compaction between levels N and N+1
Wnp1(cnt)Files written during compaction between levels N and N+1
Wnew(cnt)Wnp1 - Rnp1
Comp(sec)Compact 累計(jì)耗時(shí):micros / 1000000.0
Comp(cnt)Compact累計(jì)的次數(shù)
Avg(sec)平均每次Compact耗時(shí)
Stall(sec)level0_slowdown 耗時(shí)
Stall(cnt)level0_slowdown 累計(jì)次數(shù)
Avg(ms)平均每次Stall耗時(shí)
RecordInCompact 進(jìn)行時(shí),所有Level-N,Level-(N+1) 輸入的entries數(shù)
RecordDropCompact 進(jìn)行時(shí): RecordIn - 輸出到Level-(N+1)

http://www.cnblogs.com/svan/p/7047251.html