一、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樹與紅黑樹很類似呢?由於與紅黑樹同樣,一棵含n個結點的B樹的高度也爲O(lgn),但可能比一棵紅黑樹的高度小許多,應爲它的分支因子比較大。因此,B樹能夠在O(logn)時間內,實現各類如插入(insert),刪除(delete)等動態集合操做。
B樹的定義以下:
下圖是一個M=4的4階的B樹:
B樹的搜索:從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已是葉子結點;
B樹的特性:
B+樹是對B樹的一種變形,與B樹的差別在於:
以下圖一個M=3 的B+樹:
B+樹的搜索:與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹能夠在非葉子結點命中),其性能也等價於在關鍵字全集作一次二分查找;
B+的特性:
B樹:多路搜索樹,每一個結點存儲M/2到M個關鍵字,非葉子結點存儲指向關鍵字範圍的子結點;全部關鍵字在整顆樹中出現,且只出現一次,非葉子結點能夠命中;
B+樹:在B-樹基礎上,爲葉子結點增長鏈表指針,全部關鍵字都在葉子結點中出現,非葉子結點做爲葉子結點的索引;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樹不支持這樣的操做(或者說效率過低)。
大量的隨機寫,致使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的文件,減小無謂查找;