動(dòng)態(tài)查找樹主要有二叉查找樹(Binary Search Tree,BST),平衡二叉查找樹(Balanced Binary Search Tree,AVL), 紅黑樹 (Red-Black Tree ),都是典型的二叉查找樹結(jié)構(gòu),查找的時(shí)間復(fù)雜度都與樹的深度相關(guān),降低樹的深度會(huì)提高查找效率,于是有了多路的B-tree/B+-tree/ B*-tree。
一、 B-tree VS B+ tree
首先注意:B樹就是B-樹,"-"是個(gè)連字符號(hào),不是減號(hào)。
B-樹是一種平衡的多路查找(又稱排序)樹,在文件系統(tǒng)中有所應(yīng)用。主要用作文件的索引。其中的B就表示平衡(Balance)
B樹的優(yōu)勢(shì)是當(dāng)你要查找的值恰好處在一個(gè)非葉子節(jié)點(diǎn)時(shí),查找到該節(jié)點(diǎn)就會(huì)成功并結(jié)束查詢,而B+樹由于非葉節(jié)點(diǎn)只是索引部分,這些節(jié)點(diǎn)中只含有其子樹中的最大(或最小)關(guān)鍵字,當(dāng)非終端節(jié)點(diǎn)上的關(guān)鍵字等于給點(diǎn)值時(shí),查找并不終止,而是繼續(xù)向下直到葉子節(jié)點(diǎn)。因此在B+樹中,無論查找成功與否,都是走了一條從根到葉子節(jié)點(diǎn)的路徑。
B+樹有一個(gè)最大的好處,方便掃庫(kù),B樹必須用中序遍歷的方法按序掃庫(kù),而B+樹直接從葉子結(jié)點(diǎn)挨個(gè)掃一遍就完了。
B+樹支持range-query(區(qū)間查詢)非常方便,而B樹不支持。這是數(shù)據(jù)庫(kù)選用B+樹的最主要原因。
比如要查 5-10之間的,B+樹一把到5這個(gè)標(biāo)記,再一把到10,然后串起來就行了,B樹就非常麻煩。B樹的好處,就是成功查詢特別有利,因?yàn)闃涞母叨瓤傮w要比B+樹矮。不成功的情況下,B樹也比B+樹稍稍占一點(diǎn)點(diǎn)便宜。
有很多基于頻率的搜索是選用B樹,越頻繁query的結(jié)點(diǎn)越往根上走,前提是需要對(duì)query做統(tǒng)計(jì),而且要對(duì)key做一些變化。 另外B樹也好B+樹也好,根或者上面幾層因?yàn)楸环磸?fù)query,所以這幾塊基本都在內(nèi)存中,不會(huì)出現(xiàn)讀磁盤IO,一般已啟動(dòng)的時(shí)候,就會(huì)主動(dòng)換入內(nèi)存。 mysql底層存儲(chǔ)是用B+樹實(shí)現(xiàn)的,因?yàn)閮?nèi)存中B+樹是沒有優(yōu)勢(shì)的,但是一到磁盤,B+樹的威力就出來了。
二、 B+ tree VS B* tree
B*樹 是B+樹的變體,在B+樹的非根和非葉子結(jié)點(diǎn)再增加指向兄弟的指針;B*樹定義了非葉子結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。
B+樹的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿時(shí),分配一個(gè)新的結(jié)點(diǎn),并將原結(jié)點(diǎn)中1/2的數(shù)據(jù)復(fù)制到新結(jié)點(diǎn),最后在父結(jié)點(diǎn)中增加新結(jié)點(diǎn)的指針;B+樹的分裂只影響原結(jié)點(diǎn)和父結(jié)點(diǎn),而不會(huì)影響兄弟結(jié)點(diǎn),所以它不需要指向兄弟的指針
B*樹的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿時(shí),如果它的下一個(gè)兄弟結(jié)點(diǎn)未滿,那么將一部分?jǐn)?shù)據(jù)移到兄弟結(jié)點(diǎn)中,再在原結(jié)點(diǎn)插入關(guān)鍵字,最后修改父結(jié)點(diǎn)中兄弟結(jié)點(diǎn)的關(guān)鍵字(因?yàn)樾值芙Y(jié)點(diǎn)的關(guān)鍵字范圍改變了);如果兄弟也滿了,則在原結(jié)點(diǎn)與兄弟結(jié)點(diǎn)之間增加新結(jié)點(diǎn),并各復(fù)制1/3的數(shù)據(jù)到新結(jié)點(diǎn),最后在父結(jié)點(diǎn)增加新結(jié)點(diǎn)的指針
所以,B*樹分配新結(jié)點(diǎn)的概率比B+樹要低,空間使用率更高。
三、 B+ tree VS LSM tree
LSM樹是HBase里非常有創(chuàng)意的一種數(shù)據(jù)結(jié)構(gòu),它和傳統(tǒng)的B+樹不太一樣,下面先說說B+樹。
B+ tree
B+樹大家已經(jīng)非常的熟悉,如下圖所示:
根節(jié)點(diǎn)和枝節(jié)點(diǎn)很簡(jiǎn)單,分別記錄每個(gè)葉子節(jié)點(diǎn)的最小值,并用一個(gè)指針指向葉子節(jié)點(diǎn)。
葉子節(jié)點(diǎn)里每個(gè)鍵值都指向真正的數(shù)據(jù)塊(如Oracle里的RowID),每個(gè)葉子節(jié)點(diǎn)都有前指針和后指針,這是為了做范圍查詢時(shí),葉子節(jié)點(diǎn)間可以直接跳轉(zhuǎn),從而避免再去回溯至枝和跟節(jié)點(diǎn)。
B+樹最大的性能問題是會(huì)產(chǎn)生大量的隨機(jī)IO,隨著新數(shù)據(jù)的插入,葉子節(jié)點(diǎn)會(huì)慢慢分裂,邏輯上連續(xù)的葉子節(jié)點(diǎn)在物理上往往不連續(xù),甚至分離的很遠(yuǎn),但做范圍查詢時(shí),會(huì)產(chǎn)生大量讀隨機(jī)IO。
對(duì)于大量的隨機(jī)寫也一樣,舉一個(gè)插入key跨度很大的例子,如7->1000->3->2000 ... 新插入的數(shù)據(jù)存儲(chǔ)在磁盤上相隔很遠(yuǎn),會(huì)產(chǎn)生大量的隨機(jī)寫IO。
從上面可以看出,低下的磁盤尋道速度嚴(yán)重影響性能(近些年來,磁盤尋道速度的發(fā)展幾乎處于停滯的狀態(tài))。
LSM tree
為了克服B+樹的弱點(diǎn),HBase引入了LSM樹的概念,即Log-Structured Merge-Trees。
為了更好的說明LSM樹的原理,下面舉個(gè)比較極端的例子:
現(xiàn)在假設(shè)有1000個(gè)節(jié)點(diǎn)的隨機(jī)key,對(duì)于磁盤來說,肯定是把這1000個(gè)節(jié)點(diǎn)順序?qū)懭氪疟P最快,但是這樣一來,讀就悲劇了,因?yàn)閗ey在磁盤中完全無序,每次讀取都要全掃描;
那么,為了讓讀性能盡量高,數(shù)據(jù)在磁盤中必須得有序,這就是B+樹的原理,但是寫就悲劇了,因?yàn)闀?huì)產(chǎn)生大量的隨機(jī)IO,磁盤尋道速度跟不上。
LSM樹本質(zhì)上就是在讀寫之間取得平衡,和B+樹相比,它犧牲了部分讀性能,用來大幅提高寫性能。
它的原理是把一顆大樹拆分成N棵小樹, 它首先寫入到內(nèi)存中(內(nèi)存沒有尋道速度的問題,隨機(jī)寫的性能得到大幅提升),在內(nèi)存中構(gòu)建一顆有序小樹,隨著小樹越來越大,內(nèi)存的小樹會(huì)flush到磁盤上。當(dāng)讀時(shí),由于不知道數(shù)據(jù)在哪棵小樹上,因此必須遍歷所有的小樹,但在每顆小樹內(nèi)部數(shù)據(jù)是有序的。
以上就是LSM樹最本質(zhì)的原理,有了原理,再看具體的技術(shù)就很簡(jiǎn)單了:
1)首先說說為什么要有WAL(Write Ahead Log),很簡(jiǎn)單,因?yàn)閿?shù)據(jù)是先寫到內(nèi)存中,如果斷電,內(nèi)存中的數(shù)據(jù)會(huì)丟失,因此為了保護(hù)內(nèi)存中的數(shù)據(jù),需要在磁盤上先記錄logfile,當(dāng)內(nèi)存中的數(shù)據(jù)flush到磁盤上時(shí),就可以拋棄相應(yīng)的Logfile。
2)什么是memstore, storefile?很簡(jiǎn)單,上面說過,LSM樹就是一堆小樹,在內(nèi)存中的小樹即memstore,每次flush,內(nèi)存中的memstore變成磁盤上一個(gè)新的storefile。
3)為什么會(huì)有compact?很簡(jiǎn)單,隨著小樹越來越多,讀的性能會(huì)越來越差,因此需要在適當(dāng)?shù)臅r(shí)候,對(duì)磁盤中的小樹進(jìn)行merge,多棵小樹變成一顆大樹。
參考: