https://github.com/imjoey/blog/issues/6
https://www.jianshu.com/p/99cc0df8ed21
https://juejin.im/post/5c99f0556fb9a070e82c1fcf
目錄
一、前言
LevelDB是Google出品的具有讀寫高性能的存儲引擎,其沒有采用傳統的B+ Tree(MySQL InnoDB)的存儲模型,而是使用了LSM(Log Structure Merge-Tree)。LSM模型中的一個重要組成就是稱作SSTable(Sorted String Table)的結構。本文先簡單介紹LSM中使用的幾種結構,然後着重介紹在衆多Compaction算法中的Leveled-Compaction。
二、LSM
LSM是通過將磁盤的隨機寫改爲順序寫來提高寫的性能,核心思想是把數據的添加或修改放到內存中,當內存中數據達到一定size後,然後dump(也就是變成了順序寫)到磁盤中。LSM中有MemTable、ImmutableMemTable、SSTable等幾個概念,下面分別介紹
1、MemTable
MemTable在內存中,記錄最近修改的數據。一般其內部使用SkipList結構存儲按key排序後的有序數據,當其存儲的數據達到一定size後,就變成ImmutableMemTable,同時新建一個新的MemTable;後續的數據修改操作均在新的MemTable內進行。
2、ImmutableMemTable
ImmutableMemTable就是隻讀不寫的MemTable,它與MemTable一起保證的寫操作無鎖化。其內部的數據是有序的,所以dump到磁盤時保證了高效的順序寫,形成了新的SSTable文件。
3、SSTable
SSTable內存儲的是一系列的有序鍵值對,形式如下圖所示。當SSTable文件較大時,爲了提高讀的性能,也可以生成key-offset索引,此索引一般都記錄在內存中。
4、SSTable Compaction
由於不斷有ImmutableMemTable會dump到磁盤中成爲新的SSTable文件,所以SSTable文件的數量會逐漸增多,其內部存儲的數據也會產生很多過期數據(key的舊數據),所以需要對SSTable文件進行定期Compaction,一方面減少SSTable文件的數量,同時也刪除過期的數據。
SSTable Compaction有多種算法,比如Cassandra默認的基於文件大小的Size-Tired Compaction、基於時間序列的Date-Tiered Compaction,以及Leveled Compaction。
這裏主要以Cassandra中的Leveled Compaction算法爲例來分析(這也是LevelDB名字的由來)。
Leveled Compaction
SSTable被劃分到不同的level中,詳細的Leveled Compaction算法描述如下:
-
每個SSTable文件的固定大小爲160M
-
從ImmutableMemTable創建的SSTable文件劃分到Level-0中
-
每個Level有SSTable文件數量的限制。在除了Level-0的任意Level中,兩級Level之間的SSTable文件數量呈指數級倍數。比如:Level-1中有10個SSTable文件,Level-2有100個SSTable文件
-
在除了Level-0的任意Level中,SSTable文件之間所包含的key的範圍不重疊。(也就是說,每個Level的所有SSTable文件,可以看做是一個大的SSTable文件)
-
如果Level-0中SSTable數量超過限制(比如:4),那麼自動回將這4個Level-0的SSTable文件與Level-1的所有10個SSTable文件進行Compaction。(這裏需要特別注意:level-0比較特殊,各個SSTable之間是有重疊的,所以只能將4個SSTable與Level1中所有進行整體上的Merge和切分(如原博客所述)。但level-1及其以上,各個SSTable之間沒有重疊key,當SSTable個數超過閾值時,可以只選擇一個或者多個SSTable與下一level值域對應的SSTable進行合併,由於每一級的SSTable大小相同,可以有效避免寫放大)
-
在Compaction過程中,首先對所有的SSTable文件按key進行歸併排序,然後將排序後結果寫入到新的SSTable文件中,如果SSTable文件大小到了160M上限,就新生成SSTable繼續寫。如此類推,直到寫完所有數據。
-
刪除參與Compaction的Level-0的4個和Level-1的10箇舊的SSTable文件
-
此時Level-0的SSTable便merge到Level-1中了,那麼如果Level-1的SSTable文件數量超過上限,那麼就從Level-1中選出 1 個超量的SSTable文件,然後將其與Level-2中的SSTable文件進行Compaction。
-
查看選出的Level-1 SSTable文件中key的範圍
-
從Level-2中選出能覆蓋該範圍的所有SSTable文件
-
將以上的所有SSTable文件根據上面介紹的算法繼續進行Compaction
注:一般情況下,Level-1和Level-2的Compaction,只會涉及Level-2內大概1/10的SSTable文件,這樣可以大幅降低參與Compcation的SSTable文件數量(相比於Size-Tired Compaction),進一步提升提升性能
-
-
如果Level-2中的文件數量超過限制,則繼續按照上述算法選出超量的SSTable文件與Level-3中的SSTable文件進行Compaction
三、RockDB的合併策略圖解
1、 L0 compaction
當L0的文件數量達到level0_file_num_compaction_trigger的值時,觸發L0和L1的合併。通常必須將所有L0的文件合併到L1中,因爲L0的文件的key是有交疊的(overlapping)。
L0與L1的compaction
2、 高層Compaction
當L0 compaction完成後,L1的文件總size或者文件數量可能會超過閾值,觸發L1向L2的合併。從L1至少選擇一個文件,合併到L2中key有交疊的文件中。
L1向L2合併
同樣的,合併後可能會觸發下一各level的compaction。
合併後的L2
L2向L3合併
合併後的L3也需要做Compaction.
合併後的L3
3、 並行Compaction
並行compaction
max_background_compactions控制了並行compaction的最大數量。
四、size-tired和level合併策略比較
上文已經提到,我們需要對SSTables
做合併:將多個SSTable
文件合併成一個SSTable
文件,並對同一個key,只保留最新的值。
那這裏討論的合併策略(Compaction Strategy)又是什麼呢?
A compaction strategy is what determines which of the sstables will be compacted, and when.
也就是說,合併策略是指:1)選擇什麼時候做合併;2)哪些SSTable
會合併成一個SSTable
。
目前廣泛應用的策略有兩種:size-tiered
策略和leveled
策略。
- HBase採用的是
size-tiered
策略。 - LevelDB和RocksDB採用的是
leveled
策略。 - Cassandra兩種策略都支持。
這裏簡要介紹下兩種策略的基本原理。後面研究LevelDB
源碼時再詳細描述leveled
策略。
size-tiered
策略
簡稱STCS(Size-Tiered Compaction Strategy)。其基本原理是,每當某個尺寸的SSTable
數量達到既定個數時,合併成一個大的SSTable
,如下圖所示:
它的優點是比較直觀,實現簡單,但是缺點是合併時的空間放大效應(Space Amplification)比較嚴重,具體請參考Scylla’s Compaction Strategies Series: Space Amplification in Size-Tiered Compaction。
空間放大效應,比如說數據本身只佔用2GB,但是在合併時需要有額外的8G空間才能完成合並,那空間放大就是4倍。
leveled
策略
STCS
策略之所以有嚴重的空間放大問題,主要是因爲它需要將所有SSTable文件合併成一個文件,只有在合併完成後才能刪除小的SSTable文件。那如果我們可以每次只處理小部分SSTable
文件,就可以大大改善空間放大問題了。
leveled
策略,簡稱LCS(Leveled Compaction Strategy),核心思想就是將數據分成互不重疊的一系列固定大小(例如 2 MB)的SSTable
文件,再將其分層(level)管理。對每個Level
,我們都有一份清單文件記錄着當前Level
內每個SSTable
文件存儲的key的範圍。
Level和Level的區別在於它所保存的SSTable
文件的最大數量:Level-L
最多隻能保存 10 L 個SSTable
文件(但是Level 0
是個例外,後面再說)。
注:上圖中,"run of"就表示一個系列,這些文件互不重疊,共同組成該
level
的所有數據。Level 1
有10個文件;Level 2
有100個文件;依此類推。
下面對照着上圖再詳細描述下LCS
壓縮策略:
先來看一下當Level >= 1
時的合併策略。以Level 1
爲例,當Level 1
的SSTable
數量超過10個時,我們將多餘的SSTable
轉存到Level-2
。爲了不破壞Level-2
本身的互不重疊性,我們需要將Level-2
內與這些待轉存的SSTable
有重疊的SSTable
挑出來,然後將這些SSTable
文件重新合併去重,形成新的一組SSTable
文件。如果這組新的SSTable
文件導致Level-2
的總文件數量超過100個,再將多餘的文件按照同樣的規則轉存到Level-3
。
再來看看Level 0
。Level 0
的SSTable
文件是直接從memtable
轉化來的:你沒法保證這些SSTable
互不重疊。所以,我們規定Level 0
數量不能超過4個:當達到4個時,我們將這4個文件一起處理:合併去重,形成一組互不重疊的SSTable
文件,再將其按照上一段描述的策略轉存到Level 1
。