B樹、B+樹、LSM已經它們對應的存儲引擎及應用

典型的3種存儲引擎

一、hash:mysql

表明:nosql的redis/memcachedredis

本質爲: 基於(內存中)的hash;sql

因此支持 隨機 的增刪查改,讀寫的時間複雜度O(1);數據庫

可是沒法支持順序讀寫(注,這裏指典型的hash,不是指如redis的基於跳錶的zset的其餘功能);緩存

基本效果:在不須要有序遍歷時,最優nosql

 

二、磁盤查找樹:memcached

表明:mysql性能

本質爲:基於(磁盤的)順序查找樹,B樹/B+樹;優化

基本效果:支持有序遍歷;但數據量很大後,隨機讀寫效率低(緣由往下看);spa

 

三、lsmtree:

表明:hbase/leveldb/rocksdb

本質爲: 實際落地存儲的數據按key劃分,造成有序的不一樣的文件;

結合其「先內存更新後合併落盤」的機制,儘可能達到磁盤的寫是順序寫,儘量減小隨機寫;

對於讀,需合併磁盤已有歷史數據和當前未落盤的駐於內存的更新,較慢;

基本效果:也能夠支持有序增刪查改;寫速度大幅提升;讀速度稍慢;

B樹

B樹是一種平衡多路搜索樹,B樹與紅黑樹最大的不一樣在於,B樹的結點能夠有多個子女,從幾個到幾千個。那爲何又說B樹與紅黑樹很類似呢?由於與紅黑樹同樣,一棵含n個結點的B樹的高度也爲O(lgn),但可能比一棵紅黑樹的高度小許多,應爲它的分支因子比較大。因此,B樹能夠在O(logn)時間內,實現各類如插入(insert),刪除(delete)等動態集合操做。

B樹的定義以下:

  • 根節點至少有兩個子節點
  • 每一個節點有M-1個key,而且以升序排列
  • 位於M-1和M key的子節點的值位於M-1 和M key對應的Value之間
  • 其它節點至少有M/2個子節點
  • 全部葉子結點位於同一層;

下圖是一個M=4的4階的B樹:

B樹的搜索:從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已是葉子結點;

B樹的特性:

  1. 關鍵字集合分佈在整顆樹中;
  2. 任何一個關鍵字出現且只出如今一個結點中;
  3. 搜索有可能在非葉子結點結束(樹中全部結點都存儲數據,與B+樹這一點不一樣);
  4. 其搜索性能等價於在關鍵字全集內作一次二分查找;

B+樹

B+樹是對B樹的一種變形,與B樹的差別在於:

  1. 有n棵子樹的結點中含有n個關鍵字,每一個關鍵字不保存數據,只用來索引,全部數據都保存在葉子節點。
  2. 全部的葉子結點中包含了所有關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。
  3. 全部的非終端結點能夠當作是索引部分,結點中僅含其子樹(根結點)中的最大(或最小)關鍵字。
  4. 爲全部葉子結點增長一個鏈指針,便於區間查找和遍歷。
  5. 全部關鍵字都在葉子結點出現;

以下圖一個M=3 的B+樹:

B+樹的搜索:與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹能夠在非葉子結點命中),其性能也等價於在關鍵字全集作一次二分查找;

B+的特性:

  1. 非葉子結點至關因而葉子結點的索引(稀疏索引),葉子結點至關因而存儲(關鍵字)數據的數據層;
  2. B+樹的葉子結點都是相鏈的,所以對整棵樹的遍歷只須要一次線性遍歷葉子結點便可。並且因爲數據順序排列而且相連,因此便於區間查找和搜索。而B樹則須要進行每一層的遞歸遍歷。相鄰的元素可能在內存中不相鄰,因此緩存命中性沒有B+樹好。

B樹和B+樹總結:

B樹:多路搜索樹,每一個結點存儲M/2到M個關鍵字,非葉子結點存儲指向關鍵字範圍的子結點;全部關鍵字在整顆樹中出現,且只出現一次,非葉子結點能夠命中;

B+樹:在B-樹基礎上,爲葉子結點增長鏈表指針,全部關鍵字都在葉子結點中出現,非葉子結點做爲葉子結點的索引;B+樹老是到葉子結點才命中;

爲何說B+tree比B樹更適合實際應用中操做系統的文件索引和數據庫索引?

(1) B+tree的磁盤讀寫代價更低 
B+tree的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。

舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點須要2個盤快。而B+ 樹內部結點只須要1個盤快。當須要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。

(2)B+tree的查詢效率更加穩定 
因爲非葉子結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。

(3)B樹在提升了磁盤IO性能的同時並無解決元素遍歷的效率低下的問題。正是爲了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就能夠實現整棵樹的遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做(或者說效率過低)。

LSM樹

大量的隨機寫,致使B族樹在數據很大時,出現大量磁盤IO,致使速度愈來愈慢,lsmtree是怎麼解決這個問題的:

一、儘量減小寫磁盤次數;

二、即使寫磁盤,也是儘量順序寫;

方法:

一、對數據,按key劃分爲若干level;

每個level對應若干文件,包括存在於內存中和落盤了的;

文件內key都是有序的,同級的各個文件之間,通常也有序

如leveldb/rocksdb,level0對應於內存中的數據(0.sst),後面的依次是一、二、三、...的各級文件(默認到level6級)

二、寫時,先寫對應於內存的最低level的文件;這是之因此寫的快的一個重要緣由

存在於內存的數據,也是被持久化的以防丟失;

存在於內存的數據,到達必定大小後,被合併到下一級文件落盤;

三、落盤後的各級文件,也會按期進行排序加合併(compact),合併後數據進入下一層level;

這樣的寫入操做,大多數的寫,都是對一個磁盤頁順序的寫,或者申請新磁盤頁寫,而再也不是隨機寫

總結lsmtree的寫爲何快的兩大緣由:

一、每次寫,都是在寫內存;

二、按期合併寫入磁盤,產生的寫都是按key順序寫,而不是隨機查找key再寫;

可見compact是個很重要的事情了,下面是基於lsmtree引擎的rocksdb的compact過程:

首先看一下rocksdb的各級文件組織形式:

而後,各級的每一個文件,內部都是按key有序,除了內存對應的level0文件,各級的內部文件之間,也是按key有序的;

這樣,查找一個key,很方便二分查找(固然還有bloomfilter等的進一步優化)

再而後,每一級的數據到達必定閾值時,會觸發排序歸併,簡單說,就是在兩個level的文件中,把key有重疊的部分,合併到高層level的文件裏

這個在lsmtree裏,叫數據壓縮(compact);

對於rocksdb,除了內存那個level0到level1的compact,其餘各級之間的compact是能夠並行的;一般設置較小的level0到level1的compact閾值,加快這一層的compact

良好的歸併策略的配置,使數據儘量集中在最高層(90%以上),而不是中間層,這樣有利於compact的速度;

另外,對於基於lsmtree的(rocksdb的)讀,須要在各級文件中二分查找,磁盤IO也很多,此外還須要關注level0裏的對於這個key的操做,比較明顯的優化是,經過bloomfilter略掉確定不存在該key的文件,減小無謂查找;

相關文章
相關標籤/搜索