LSM樹原理探究

bright-ecology-environment-1006115.jpg

前言

B+樹隨着mysql Innodb引擎的普遍推廣愈來愈被你們所熟知,而前不久我在研究Raft算法時,偶然發現了一種和B+樹相似的數據結構——LSM樹(Log-Structured-Merge-Tree 日誌結構合併樹),它是Google發表的論文 Big Table 中提到的一種頗有趣的文件組織數據結構, 現現在已經被運用在不少工業界的產品之中了:HBase、Cassandra、LevelDB、RocksDB等等。今天就來研究研究LSM樹的原理。html

LSM樹定義

LSM樹(Log-Structured-Merge-Tree)和B+樹相似,它們被設計出來都是爲了更好地將數據存儲到大容量磁盤中。相對於B+樹,LSM樹擁有更好的隨機寫性能。在下面的一個ACM的報告中能夠看到:mysql

ACM-report.jpg

磁盤順序寫的性能有些顛覆咱們的常識的,在上面的例子中,磁盤順序寫的吞吐量甚至可以超過內存隨即寫的吞吐量。而LSM樹正是利用了這一點,它經過將磁盤隨機寫操做轉化爲順序寫操做,從而將隨機寫操做的吞吐量提升了好幾個數量級。那麼它是如何轉換的呢?下面咱們先來看看它的基本思想。算法

LSM樹的基本思想

LSM樹會將全部的數據插入、修改、刪除等操做保存在內存之中,當此類操做達到必定的數據量後,再批量地寫入到磁盤當中。而在寫入磁盤時,會和之前的數據作合併。在合併過程當中,並不會像B+樹同樣,在原數據的位置上修改,而是直接插入新的數據,從而避免了隨機寫。sql

LSM樹的結構

LSM樹的結構是橫跨內存和磁盤的,包含memtable、immutable memtable、SSTable等多個部分。數據庫

memtable

顧名思義,memtable是在內存中的數據結構,用以保存最近的一些更新操做,當寫數據到memtable中時,會先經過WAL的方式備份到磁盤中,以防數據由於內存掉電而丟失。數據結構

預寫式日誌(Write-ahead logging,縮寫 WAL)是關係數據庫系統中用於提供原子性和持久性(ACID屬性中的兩個)的一系列技術。在使用WAL的系統中,全部的修改在提交以前都要先寫入log文件中。函數

memtable可使用跳躍表或者搜索樹等數據結構來組織數據以保持數據的有序性。當memtable達到必定的數據量後,memtable會轉化成爲immutable memtable,同時會建立一個新的memtable來處理新的數據。性能

immutable memtable

顧名思義,immutable memtable在內存中是不可修改的數據結構,它是將memtable轉變爲SSTable的一種中間狀態。目的是爲了在轉存過程當中不阻塞寫操做。寫操做能夠由新的memtable處理,而不用由於鎖住memtable而等待。大數據

SSTable

SSTable(Sorted String Table)即爲有序鍵值對集合,是LSM樹組在磁盤中的數據的結構。若是SSTable比較大的時候,還能夠根據鍵的值創建一個索引來加速SSTable的查詢。下圖是一個簡單的SSTable結構示意:設計

SSTable.png

memtable中的數據最終都會被轉化爲SSTable並保存在磁盤中,後續還會有相應的SSTable日誌合併操做,也是LSM樹結構的重點。

最終LSM樹的結構能夠由下圖簡單表示:

LSM樹結構.jpg

LSM樹的增刪改查

上面介紹了LSM的基本思想和結構,下面來看看它們是怎麼配合起來完成一個數據系統最多見的CRUD流程。

寫入操做

寫操做首先須要經過WAL將數據寫入到磁盤Log中,防止數據丟失,而後數據會被寫入到內存的memtable中,這樣一次寫操做即已經完成了,只須要1次磁盤IO,再加1次內存操做。相較於B+樹的屢次磁盤隨機IO,大大提升了效率。隨後這些在memtable中的數據會被批量的合併到磁盤中的SSTable當中,將隨機寫變爲了順序寫。

刪除操做

當有刪除操做時,並不須要像B+樹同樣,在磁盤中的找到相應的數據後再刪除,只須要在memtable中插入一條數據看成標誌,如delKey:1933,當讀操做讀到memtable中的這個標誌時,就會知道這個key已被刪除。隨後在日誌合併中,這條被刪除的數據會在合併的過程當中一塊兒被刪除。

更新操做

更新操做和000刪除操做相似,都是隻操做memtable,寫入一個標誌,隨後真正的更新操做被延遲在合併時一併完成。

查詢操做

查詢操做相較於B+樹就會很慢了,讀操做須要依次讀取memtable、immutable memtable、SSTable0、SSTable1......。須要反序地遍歷全部的集合,又由於寫入順序和合並順序的緣故,序號小的集合中的數據必定會比序號大的集合中的數據新。因此在這個反序遍歷的過程當中一旦匹配到了要讀取的數據,那麼必定是最新的數據,只要返回該數據便可。可是若是一個數據的確不在全部的數據集合中,則會白白得遍歷一遍。

讀操做看上去比較笨拙,所幸能夠經過布隆過濾器來加速讀操做。當布隆過濾器顯示相應的SSTable中沒有要讀取的數據時,就跳過該SSTable。

布隆過濾器其實是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除困難。

還有上面提到的索引文件,也能夠加速讀操做。

合併操做

由前面的增刪改查操做來看,合併操做是LSM樹最重要的操做。

合併操做有兩個主要的做用:

  1. 合併內存中的數據到磁盤中。
  2. 因爲將內存數據合併到磁盤當中會產生大量的小的集合,而且更新和刪除操做會產生大量的冗餘數據,經過合併操做能夠減小集合中的冗餘數據並下降讀操做時線性掃描的耗時。

目前普遍使用的有兩種合併策略,size-tiered策略和leveled策略

size-tiered策略

size-tiered策略是HBase採用的合併策略,具體內容是當某個規模的集合達到必定的數量時,將這些集合合併爲一個大的集合。好比有5個50個數據的集合,那麼就將他們合併爲一個250個數據的集合。這種策略有一個缺點是當集合達到必定的數據量後,合併操做會變得十分的耗時。

leveled策略

leveled策略是LevelDB和RocksDB採用的合併策略,size-tiered策略由於會產生大數據量的集合,因此會形成突發的IO和CPU資源的消耗,因此leveled策略使用了分層的數據結構來代替原來的大數據集合。

leveled策略將集合的大小限制在一個小的範圍內如5MB,並且將集合劃分爲不一樣的層級。每個層級的集合總大小是固定且遞增的。如第一層爲50MB,第二層爲500MB...。當某一層的數據集合大小達到上限時,就會從這一層中選出一個文件和下一層合併,或者直接提高到下一層。若是在合併過程當中發現了數據衝突,則丟棄下一層的數據,由於低層的數據老是更新的。

同時leveled策略會限制,除第一層外。其餘的每一層的鍵值都不會重複。這是經過合併時剔除冗餘數據實現的,以此來加速在同一層內數據的線性掃描速度。

RocksDB Compaction實例

結論

LSM樹犧牲了小部分讀性能,而大幅度提升了寫性能,因此很適合寫多讀少的場景,在這種場景下比B+樹更加可以勝任。經過深刻了解,能夠發現LSM樹的合併策略會大大影響到LSM樹的性能,因此應該根據具體的場景,靈活地選擇相應的策略。


邀舞卡王老魔的代碼備忘錄

相關文章
相關標籤/搜索