LSM樹的不同實現介紹

飛奔的蛋蛋發表於2019-03-04

這個需要和BTree一起來講,我們都知道BTree是Balance Tree,為了維持Balancing的特性,每次讀入的時候需要對樹進行調整,是一筆不小的時間損耗。在一些使用場景下(例如google爬取網頁),需要高效能寫入,而對讀的要求並不是那麼高。 因此,LSM樹是基於這樣的背景下發展出來的,針對類似的場景LSM提出了很多優化措施。

應用場景

舉幾個比較常見的例子:HBase、LevelDB、RocksDB

HBase大家可能比較熟,LevelDB和RocksDB分別是Google和FaceBook推出的,同樣也是開源的。對於英語好的同學可以直接看看官方的文件,對於LSM的工業實現會有一個更深入的理解。

為什麼要看不同的DB的實現呢?因為雖然是同個資料結構,但是在實現上通過類似布隆過濾器等手段,不同的實現方式能夠大大提升其效率,因此瞭解不同公司的LSM樹實現還是很有必要的。

LSM樹基礎

LSM Tree,全寫Log-Structured Merge Tree。本質上通過順序寫入log的方式,大大的提升寫入的效能,同時NoSQL的結構屬性幫助其天然適合Shard。

基本操作

1.將資料寫入記憶體memtable(同時硬碟中複製一份,保證系統當機資料不丟失) 2.資料到達一定容量,將資料flush到硬碟SStable,同時對硬碟中的資料進行merge

複雜之處:Compaction

SStable在硬碟中不斷增多,會帶來讀的巨大壓力。這時候就需要“資料結構”所獨特的價值之處了,通過不同的Compaction(壓縮)來構建不同的資料結構,能夠有效的減低讀的效率。 用空間來換取時間,這也是HBase、LevelDB、RocksDB三者之間的真正區別所在。

HBase中的LSM實現

先來看下HBase中的實現,如圖所示。

hbase_lsm 網路上很多提到了Tree的概念,其實HBase的實現並沒有依託於Tree。大家肯定有個疑問,“HBase那不是會很慢?”事實上肯定不慢,正是靠著布隆過濾器,HBase很好的詮釋了資料結構時間和空間之間的藝術。

布隆過濾器

布隆過濾器和動態快取有點像,不過都是資料結構很典型的例子,使用空間大幅度的降低了時間的使用。 bloom

bloom 藉助的是一個大大大的位表以及多個hash函式,例如圖中,將{x,y,z}的用3個hash函式對映到位表中。那麼位表中有n個位置有標1,(3<=n<=9)。

  1. 如果這個時候來了個w,w經過3個hash函式,對應到位表上的位置有一個對映的值為0,那麼說明w肯定不在{x,y,x}中。
  2. 如果w的3個hash函式對映後,都為1,並不能夠說明w在{x,y,z}中。有可能是類似上圖4,5,6位置格,而那些格是有不同的值對映出來的。

HBase的Compaction

如同上文所講,Compaction才是一個LSM樹的核心。HBase中分為兩種:

  • Minor Compaction
  • Major Compaction

兩者都是合併SStable(在HBase中SStable叫HFile)。Major Compaction在合併的時候回刪除一些過期的Key,持續時間比較長,一般在業務低峰期手動觸發。(業務低峰期可使用hbase.offpeak.start.hour和hbase.offpeak.end.hour配置)

同時,不同版本的HBase也提供了不同的Compaction策略讓使用者來決策:

  • RatioBasedCompactionPolicy
  • ExploringCompactionPolicy

兩種具體介紹看:http://hbasefly.com/2016/07/13/hbase-compaction-1/

因為筆者沒有實際使用過HBase的經驗,所以不敢大加妄論,各位看官可以通過上面的連線看看比較專業的介紹。但是,對於筆者這樣的業務方而言,HBase沒有提供索引,而是通過Scanner去查詢資料,似乎這個資料結構不夠“完美”。

LevelDB and RocksDB

RocksDb是facebook團隊基於google開發的LevelDB的升級版本,提供了很多LevelDB所不具備的feature。(延伸一下,TiDB是一個newSQL資料庫,它的底層就是基於RocksDb。網上已經有很多公司已經將TiDB上到生產環境,筆者所在公司的資料團隊也已經灰度的一部分資料到TiDB上了。說明RocksDb也是挺可靠的。) LevelDB的文件比較少,所以一起來看RocksDB的實現吧。

LevelDB文件地址: https://github.com/google/leveldb/blob/master/doc/impl.md RocksDB文件地址: https://github.com/facebook/rocksdb/wiki

Compactions

RocksDB也提供了多種的策略:

  • Leveled style compaction
  • Universal style compaction
  • FIFO Compaction

這裡主要介紹下Leveled style compaction,因為他是起源於LevelDB,方便大家和LevelDB一同瞭解。

Leveled style compaction

rocksdb1

如上圖,可以看到Leveled style將SStable劃分為不同的Level,除了Level 1中可能存在重複的key之外,Level 2之後都不會有重複的key。同時每個SStable中,key都是有序的,不要小看了有序的作用,看到查詢你就會知道他的妙用。

rocksdb2 另外,每一層的容量是遞增的,這個也比較好理解,如上圖。 rocksdb3

如上圖,compaction的過程其實也比較簡單,當某一個level的數量大於容量的時候,會選一個SStable與level+1的資料進行merge。選取的level+1的SStable是存在和當前SStable存在相同key的。如果生成的SStable導致level+1的容量也超過限度,那麼繼續往下merge。

比較吊的是,除了level0和level1的merge,其他的merge都是可以多執行緒一起進行的,因為是沒有重複key的,所以不用擔心併發問題。

查詢

就像上面說的有序效能夠極大的提升key查詢的效率,查詢通過兩步:

  1. 二分查詢所有檔案的start和end,找到可能存在key的file
  2. 二分查詢file中所有包含的key

所有的查詢都是通過二分查詢來實現的。

總結

本文從LSM介紹入手,概覽了一些LSM的工程實現。由於還沒深入瞭解這幾種DB的原始碼,僅僅只能從文件中找到一些有價值的內容給大家看,希望能夠拋磚迎玉,讓大家重新體會到資料結構的妙用。如有內容不對或者想交流一下,請隨時聯絡我~

相關文章