1. 程式人生 > >LSM樹

LSM樹

red hba 由來 一個 追加 復用 提高 key map

LSM樹(Log-Structured Merge Tree)存儲引擎

代表數據庫:nessDB、leveldb、hbase等

核心思想的核心就是放棄部分讀能力,換取寫入的最大化能力。LSM Tree ,這個概念就是結構化合並樹的意思,它的核心思路其實非常簡單,就是假定內存足夠大,因此不需要每次有數據更新就必須將數據寫入到磁盤中,而可以先將最新的數據駐留在磁盤中,等到積累到最後多之後,再使用歸並排序的方式將內存內的數據合並追加到磁盤隊尾(因為所有待排序的樹都是有序的,可以通過合並排序的方式快速合並到一起)。

日誌結構的合並樹(LSM-tree)是一種基於硬盤的數據結構,與B-tree相比,能顯著地減少硬盤磁盤臂的開銷,並能在較長的時間提供對文件的高速插入(刪除)。然而LSM-tree在某些情況下,特別是在查詢需要快速響應時性能不佳。通常LSM-tree適用於索引插入比檢索更頻繁的應用系統。Bigtable在提供Tablet服務時,使用GFS來存儲日誌和SSTable,而GFS的設計初衷就是希望通過添加新數據的方式而不是通過重寫舊數據的方式來修改文件。而LSM-tree通過滾動合並和多頁塊的方法推遲和批量進行索引更新,充分利用內存來存儲近期或常用數據以降低查找代價,利用硬盤來存儲不常用數據以減少存儲代價。

磁盤的技術特性:對磁盤來說,能夠最大化的發揮磁盤技術特性的使用方式是:一次性的讀取或寫入固定大小的一塊數據,並盡可能的減少隨機尋道這個操作的次數。

技術分享圖片

LSM和Btree差異就要在讀性能和寫性能進行舍和求。在犧牲的同事,尋找其他方案來彌補。

1、LSM具有批量特性,存儲延遲。當寫讀比例很大的時候(寫比讀多),LSM樹相比於B樹有更好的性能。因為隨著insert操作,為了維護B樹結構,節點分裂。讀磁盤的隨機讀寫概率會變大,性能會逐漸減弱。 多次單頁隨機寫,變成一次多頁隨機寫,復用了磁盤尋道時間,極大提升效率。

2、B樹的寫入過程:對B樹的寫入過程是一次原位寫入的過程,主要分為兩個部分,首先是查找到對應的塊的位置,然後將新數據寫入到剛才查找到的數據塊中,然後再查找到塊所對應的磁盤物理位置,將數據寫入去。當然,在內存比較充足的時候,因為B樹的一部分可以被緩存在內存中,所以查找塊的過程有一定概率可以在內存內完成,不過為了表述清晰,我們就假定內存很小,只夠存一個B樹塊大小的數據吧。可以看到,在上面的模式中,需要兩次隨機尋道(一次查找,一次原位寫),才能夠完成一次數據的寫入,代價還是很高的。

3、LSM Tree放棄磁盤讀性能來換取寫的順序性,似乎會認為讀應該是大部分系統最應該保證的特性,所以用讀換寫似乎不是個好買賣。但別急,聽我分析一下。

a、內存的速度遠超磁盤,1000倍以上。而讀取的性能提升,主要還是依靠內存命中率而非磁盤讀的次數

b、寫入不占用磁盤的io,讀取就能獲取更長時間的磁盤io使用權,從而也可以提升讀取效率。例如LevelDb的SSTable雖然降低了了讀的性能,但如果數據的讀取命中率有保障的前提下,因為讀取能夠獲得更多的磁盤io機會,因此讀取性能基本沒有降低,甚至還會有提升。而寫入的性能則會獲得較大幅度的提升,基本上是5~10倍左右。

下面說說詳細例子:

LSM Tree弄了很多個小的有序結構,比如每m個數據,在內存裏排序一次,下面100個數據,再排序一次……這樣依次做下去,我就可以獲得N/m個有序的小的有序結構。

在查詢的時候,因為不知道這個數據到底是在哪裏,所以就從最新的一個小的有序結構裏做二分查找,找得到就返回,找不到就繼續找下一個小有序結構,一直到找到為止。

很容易可以看出,這樣的模式,讀取的時間復雜度是(N/m)*log2N 。讀取效率是會下降的。

這就是最本來意義上的LSM tree的思路。那麽這樣做,性能還是比較慢的,於是需要再做些事情來提升,怎麽做才好呢?

LSM Tree優化方式:

a、Bloom filter: 就是個帶隨即概率的bitmap,可以快速的告訴你,某一個小的有序結構裏有沒有指定的那個數據的。於是就可以不用二分查找,而只需簡單的計算幾次就能知道數據是否在某個小集合裏啦。效率得到了提升,但付出的是空間代價。

b、compact:小樹合並為大樹:因為小樹他性能有問題,所以要有個進程不斷地將小樹合並到大樹上,這樣大部分的老數據查詢也可以直接使用log2N的方式找到,不需要再進行(N/m)*log2n的查詢了

  • 哈希存儲引擎 是哈希表的持久化實現,支持增、刪、改以及隨機讀取操作,但不支持順序掃描,對應的存儲系統為key-value存儲系統。對於key-value的插入以及查詢,哈希表的復雜度都是O(1),明顯比樹的操作O(n)快,如果不需要有序的遍歷數據,哈希表就是your Mr.Right
  • B樹存儲引擎是B樹(關於B樹的由來,數據結構以及應用場景可以看之前一篇博文)的持久化實現,不僅支持單條記錄的增、刪、讀、改操作,還支持順序掃描(B+樹的葉子節點之間的指針),對應的存儲系統就是關系數據庫(Mysql等)。
  • LSM樹(Log-Structured Merge Tree)存儲引擎和B樹存儲引擎一樣,同樣支持增、刪、讀、改、順序掃描操作。而且通過批量存儲技術規避磁盤隨機寫入問題。當然凡事有利有弊,LSM樹和B+樹相比,LSM樹犧牲了部分讀性能,用來大幅提高寫性能。

通過以上的分析,應該知道LSM樹的由來了,LSM樹的設計思想非常樸素:將對數據的修改增量保持在內存中,達到指定的大小限制後將這些修改操作批量寫入磁盤,不過讀取的時候稍微麻煩,需要合並磁盤中歷史數據和內存中最近修改操作,所以寫入性能大大提升,讀取時可能需要先看是否命中內存,否則需要訪問較多的磁盤文件。極端的說,基於LSM樹實現的HBase的寫性能比MySQL高了一個數量級,讀性能低了一個數量級。

LSM樹原理把一棵大樹拆分成N棵小樹,它首先寫入內存中,隨著小樹越來越大,內存中的小樹會flush到磁盤中,磁盤中的樹定期可以做merge操作,合並成一棵大樹,以優化讀性能。

技術分享圖片

以上這些大概就是HBase存儲的設計主要思想,這裏分別對應說明下:

  • 因為小樹先寫到內存中,為了防止內存數據丟失,寫內存的同時需要暫時持久化到磁盤,對應了HBase的MemStore和HLog
  • MemStore上的樹達到一定大小之後,需要flush到HRegion磁盤中(一般是Hadoop DataNode),這樣MemStore就變成了DataNode上的磁盤文件StoreFile,定期HRegionServer對DataNode的數據做merge操作,徹底刪除無效空間,多棵小樹在這個時機合並成大樹,來增強讀性能。

關於LSM Tree,對於最簡單的二層LSM Tree而言,內存中的數據和磁盤你中的數據merge操作,如下圖

技術分享圖片

圖來自lsm論文

lsm tree,理論上,可以是內存中樹的一部分和磁盤中第一層樹做merge,對於磁盤中的樹直接做update操作有可能會破壞物理block的連續性,但是實際應用中,一般lsm有多層,當磁盤中的小樹合並成一個大樹的時候,可以重新排好順序,使得block連續,優化讀性能。

hbase在實現中,是把整個內存在一定閾值後,flush到disk中,形成一個file,這個file的存儲也就是一個小的B+樹,因為hbase一般是部署在hdfs上,hdfs不支持對文件的update操作,所以hbase這麽整體內存flush,而不是和磁盤中的小樹merge update,這個設計也就能講通了。內存flush到磁盤上的小樹,定期也會合並成一個大樹。整體上hbase就是用了lsm tree的思路。

LSM樹