一文搞懂LSM(Log-structured Merge Tree)


一、LSM樹數據結構定義

查閱了一些資料,LSM樹並沒有一種固定死的實現方式,更多的是一種將:

“磁盤順序寫” + “多個樹(狀數據結構)” + “冷熱(新老)數據分級” + “定期歸並” + “非原地更新”這幾種特性統一在一起的思想。

為了方便后續的講解分析,我們嘗試先對LSM樹做一個定義。

LSM樹的定義:

  1. LSM樹是一個橫跨內存和磁盤的,包含多顆"子樹"的一個森林。
  2. LSM樹分為Level 0,Level 1,Level 2 ... Level n 多顆子樹,其中只有Level 0在內存中,其余Level 1-n在磁盤中。
  3. 內存中的Level 0子樹一般采用排序樹(紅黑樹/AVL樹)、跳表或者TreeMap等這類有序的數據結構,方便后續順序寫磁盤。
  4. 磁盤中的Level 1-n子樹,本質是數據排好序后順序寫到磁盤上的文件,只是叫做樹而已。
  5. 每一層的子樹都有一個閾值大小,達到閾值后會進行合並,合並結果寫入下一層。
  6. 只有內存中數據允許原地更新,磁盤上數據的變更只允許追加寫,不做原地更新。

以上6條定義組成了LSM樹,如圖1所示。

img

​ 圖1 LSM樹的組成和定義(狗日的知乎把我高清圖片壓縮了。。。)

  • 圖1中分成了左側綠色的內存部分和右側藍色的磁盤部分(定義1)。
  • 圖1左側綠色的內存部分只包含Level 0樹,右側藍色的磁盤部分則包含Level 1-n等多棵"樹"(定義2)
  • 圖1左側綠色的內存部分中Level 0是一顆二叉排序樹(定義3)。注意這里的有序性,該性質決定了LSM樹優異的讀寫性能。
  • 圖1右側藍色的磁盤部分所包含的Level 1到Level n多顆樹,雖然叫做“樹”,但本質是按數據key排好序后,順序寫在磁盤上的一個個文件(定義4) ,注意這里再次出現了有序性。
  • 內存中的Level 0樹在達到閾值后,會在內存中遍歷排好序的Level 0樹並順序寫入磁盤的Level 1。同樣的,在磁盤中的Level n(n>0)達到閾值時,則會將Level n層的多個文件進行歸並,寫入Level n+1層。(定義5)
  • 除了內存中的Level 0層做原地更新外,對已寫入磁盤上的數據,都采用Append形式的磁盤順序寫,即更新和刪除操作並不去修改老數據,只是簡單的追加新數據。圖1中右側藍色的磁盤部分,Level 1和Level 2均包含key為2的數據,同時圖1左側綠色內存中的Level 0樹也包含key為2的數據節點。(定義6)

下面我們遵循LSM樹的6條定義,通過動圖對LSM樹的增、刪、改、查和歸並進行詳細分析。

二、插入操作

LSM樹的插入較簡單,數據無腦往內存中的Level 0排序樹丟即可,並不關心該數據是否已經在內存或磁盤中存在。(已經存在該數據的話,則場景轉換成更新操作,詳見第四部分)

圖2展示了,新數據直接插入Level 0樹的過程。

圖2 LSM樹的插入操作示例

如上圖2所示,我們依次插入了key=9、1、6的數據,這三個數據均按照key的大小,插入內存里的Level 0排序樹中。該操作復雜度為樹高log(n),n是Level 0樹的數據量,可見代價很低,能實現極高的寫吞吐量。

三、刪除操作

LSM樹的刪除操作並不是直接刪除數據,而是通過一種叫“墓碑標記”的特殊數據來標識數據的刪除。

刪除操作分為:待刪除數據在內存中、待刪除數據在磁盤中 和 該數據根本不存在 三種情況。

3.1 待刪除數據在內存中:

如圖3所示,展示了待刪除數據在內存中的刪除過程。我們不能簡單地將Level 0樹中的黃色節點2刪除,而是應該采用墓碑標記將其覆蓋(思考題:為什么不能直接刪除而是要用墓碑標記覆蓋呢)

img

​ 圖3 LSM樹刪除操作示例——待刪除數據在內存中時

3.2 待刪除數據在磁盤中:

如圖4所示,展示了待刪除數據在磁盤上時的刪除過程。我們並不去修改磁盤上的數據(理都不理它),而是直接向內存中的Level 0樹中插入墓碑標記即可。

img

​ 圖4 LSM樹刪除操作示例——待刪除數據在磁盤中時

3.3 待刪除數據根本不存在:

這種情況等價於在內存的Level 0樹中新增一條墓碑標記,場景轉換為情況3.2的內存中插入墓碑標記操作。

綜合看待上述三種情況,發現不論數據有沒有、在哪里,刪除操作都是等價於向Level 0樹中寫入墓碑標記。該操作復雜度為樹高log(n),代價很低。

四、修改操作

LSM樹的修改操作和刪除操作很像,也是分為三種情況:待修改數據在內存中、在磁盤中和 該數據根本不存在。

4.1 待修改數據在內存中:

img

​ 圖5 LSM樹修改操作示例——待修改數據在內存中時

如圖5所示,展示了待修改數據在內存中的操作過程。新的藍色的key=7的數據,直接定位到內存中Level 0樹上黃色的老的key=7的位置,將其覆蓋即可。

4.2 待修改數據在磁盤中:

img

​ 圖6 LSM樹修改操作示例——待修改數據在磁盤中時

如圖6所示,展示了待修改數據在磁盤中的操作過程。LSM樹並不會去磁盤中的Level 1樹上原地更新老的key=7的數據,而是直接將新的藍色的節點7插入內存中的Level 0樹中。

4.3 該數據根本不存在:

此場景等價於情況b,直接向內存中的Level 0樹插入新的數據即可。

綜上4.1、4.2、4.3三種情況可以看出,修改操作都是對內存中Level 0進行覆蓋/新增操作。該操作復雜度為樹高log(n),代價很低。

我們會發現,LSM樹的增加、刪除、修改(這三個都屬於寫操作)都是在內存中倒騰,完全沒涉及到磁盤操作,所以速度飛快,寫吞吐量高的離譜。。。

五、查詢操作

LSM樹的查詢操作會按順序查找Level 0、Level 1、Level 2 ... Level n 每一顆樹,一旦匹配便返回目標數據,不再繼續查詢。該策略保證了查到的一定是目標key最新版本的數據(有點MVCC的感覺)。

我們來分場景分析:依然分為 待查詢數據在內存中 和 待查詢數據在磁盤中 兩種情況。

5.1 待查詢數據在內存中:

如圖7所示,展示了待查詢數據在內存中時的查詢過程。

img

​ 圖7 LSM樹查詢操作示例——待查詢數據在內存中時

沿着內存中已排好序的Level 0樹遞歸向下比較查詢,返回目標節點即可。我們注意到磁盤上的Level 1樹中同樣包括一個key=6的較老的數據。但LSM樹查詢的時候會按照Level 0、1、2 ... n的順序查詢,一旦查到第一個就返回,因此磁盤上老的key=6的數據沒人理它,更不會作為結果被返回。

5.2 待查詢數據在磁盤中:

如圖8所示,展示了待查詢數據在磁盤上時的查詢過程。

​ 圖8 LSM樹查詢操作示例——待查詢數據在磁盤中時

先查詢內存中的Level 0樹,沒查到便查詢磁盤中的Level 1樹,還是沒查到,於是查詢磁盤中的Level 2樹,匹配后返回key=6的數據。

綜合上述兩種情況,我們發現,LSM樹的查詢操作相對來說代價比較高,需要從Level 0到Level n一直順序查下去。極端情況是LSM樹中不存在該數據,則需要把整個庫從Level 0到Level n給掃了一遍,然后返回查無此人(可以通過 布隆過濾器 + 建立稀疏索引 來優化查詢操作)。代價大於以B/B+樹為基本數據結構的傳統RDB存儲引擎。

六、合並操作

合並操作是LSM樹的核心(畢竟LSM樹的名字就叫: 日志結構合並樹,直接點名了合並這一操作)

之所以在增、刪、改、查這四個基本操作之外還需要合並操作:一是因為內存不是無限大,Level 0樹達到閾值時,需要將數據從內存刷到磁盤中,這是合並操作的第一個場景;二是需要對磁盤上達到閾值的順序文件進行歸並,並將歸並結果寫入下一層,歸並過程中會清理重復的數據和被刪除的數據(墓碑標記)。我們分別對上述兩個場景進行分析:

6.1 內存數據寫入磁盤的場景:

如圖9所示,展示了內存中Level 0樹在達到閾值后,歸並寫入磁盤Level 1樹的場景。

img

​ 圖9 LSM樹合並操作示例——內存數據寫入磁盤

對內存中的Level 0樹進行中序遍歷,將數據順序寫入磁盤的Level 1層即可,我們可以看到因為Level 0樹是已經排好序的,所以寫入的Level 1中的新塊也是有序的(有序性保證了查詢和歸並操作的高效)。此時磁盤的Level 1層有兩個Block塊。

6.2 磁盤中多個塊的歸並:

如圖10所示,該圖展示了磁盤中Level 1層達到閾值時,對其包含的兩個Block塊進行歸並,並將歸並結果寫入Level 2層的過程。

​ 圖10 LSM樹合並操作示例——磁盤上的多個塊合並

我們注意到key=5和key=7的數據同時存在於較老的Block 1和較新的Block 2中。而歸並的過程是保留較新的數據,於是我們看到結果中,key=5和7的數據都是紅色的(來自於較新的Block2)。

綜上我們可以看到,不論是場景6.1還是場景6.2,由於原始數據都是有序的,因此歸並的過程只需要對數據集進行一次掃描即可,復雜度為O(n)。

七、優缺點分析

以上便是對LSM樹的增、刪、改、查和歸並五種核心操作的詳細分析。

可以看到LSM樹將增、刪、改這三種操作都轉化為內存insert + 磁盤順序寫(當Level 0滿的時候),通過這種方式得到了無與倫比的寫吞吐量。

LSM樹的查詢能力則相對被弱化,相比於B+樹的最多3~4次磁盤IO,LSM樹則要從Level 0一路查詢Level n,極端情況下等於做了全表掃描。(即便做了稀疏索引,也是lg(N0)+lg(N1)+...+lg(Nn)的復雜度,大於B+樹的lg(N0+N1+...+Nn)的時間復雜度)。

同時,LSM樹只append追加不原地修改的特性引入了歸並操作,歸並操作涉及到大量的磁盤IO,比較消耗性能,需要合理設置觸發該操作的參數。

綜上我們可以給出LSM樹的優缺點:

優:增、刪、改操作飛快,寫吞吐量極大。

缺:讀操作性能相對被弱化;不擅長區間范圍的讀操作; 歸並操作較耗費資源。

LSMTree的增、刪、改、查四種基本操作的時間復雜度分析如下所示:

操作 平均代價 最壞情況代價
插入 1 1
刪除 1 1
修改 1 1
查找 lgN lgN

八、總結

以上是對LSM樹基本操作以及優缺點的分析,我們可以據此得出LSM樹的設計原則:

  1. 先內存再磁盤
  2. 內存原地更新
  3. 磁盤追加更新
  4. 歸並保留新值

如果說B/B+樹的讀寫性能基本平衡的話,LSM樹的設計原則通過舍棄部分讀性能,換取了無與倫比的寫性能。該數據結構適合用於寫吞吐量遠遠大於讀吞吐量的場景,得到了NoSQL屆的喜愛和好評。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM