在MySQL中,無論是Innodb還是MyIsam,都使用了B+樹作索引結(jié)構(gòu)(這里不考慮hash等其他索引)。本文將從最普通的二叉查找樹開始,逐步說明各種樹解決的問題以及面臨的新問題,從而說明MySQL為什么選擇B+樹作為索引結(jié)構(gòu)。
二叉查找樹(BST,Binary Search Tree),也叫二叉排序樹,在二叉樹的基礎(chǔ)上需要滿足:任意節(jié)點的左子樹上所有節(jié)點值不大于根節(jié)點的值,任意節(jié)點的右子樹上所有節(jié)點值不小于根節(jié)點的值。如下是一顆BST:
當(dāng)需要快速查找時,將數(shù)據(jù)存儲在BST是一種常見的選擇,因為此時查詢時間取決于樹高,平均時間復(fù)雜度是O(lgn)。然而,BST可能長歪而變得不平衡,如下圖所示,此時BST退化為鏈表,時間復(fù)雜度退化為O(n)。
為了解決這個問題,引入了平衡二叉樹。
AVL樹是嚴(yán)格的平衡二叉樹,所有節(jié)點的左右子樹高度差不能超過1;AVL樹查找、插入和刪除在平均和最壞情況下都是O(lgn)。
AVL實現(xiàn)平衡的關(guān)鍵在于旋轉(zhuǎn)操作:插入和刪除可能破壞二叉樹的平衡,此時需要通過一次或多次樹旋轉(zhuǎn)來重新平衡這個樹。當(dāng)插入數(shù)據(jù)時,最多只需要1次旋轉(zhuǎn)(單旋轉(zhuǎn)或雙旋轉(zhuǎn));但是當(dāng)刪除數(shù)據(jù)時,會導(dǎo)致樹失衡,AVL需要維護從被刪除節(jié)點到根節(jié)點這條路徑上所有節(jié)點的平衡,旋轉(zhuǎn)的量級為O(lgn)。
由于旋轉(zhuǎn)的耗時,AVL樹在刪除數(shù)據(jù)時效率很低;在刪除操作較多時,維護平衡所需的代價可能高于其帶來的好處,因此AVL實際使用并不廣泛。
與AVL樹相比,紅黑樹并不追求嚴(yán)格的平衡,而是大致的平衡:只是確保從根到葉子的最長的可能路徑不多于最短的可能路徑的兩倍長。從實現(xiàn)來看,紅黑樹最大的特點是每個節(jié)點都屬于兩種顏色(紅色或黑色)之一,且節(jié)點顏色的劃分需要滿足特定的規(guī)則(具體規(guī)則略)。紅黑樹示例如下:
與AVL樹相比,紅黑樹的查詢效率會有所下降,這是因為樹的平衡性變差,高度更高。但紅黑樹的刪除效率大大提高了,因為紅黑樹同時引入了顏色,當(dāng)插入或刪除數(shù)據(jù)時,只需要進行O(1)次數(shù)的旋轉(zhuǎn)以及變色就能保證基本的平衡,不需要像AVL樹進行O(lgn)次數(shù)的旋轉(zhuǎn)??偟膩碚f,紅黑樹的統(tǒng)計性能高于AVL。
因此,在實際應(yīng)用中,AVL樹的使用相對較少,而紅黑樹的使用非常廣泛。例如,Java中的TreeMap使用紅黑樹存儲排序鍵值對;Java8中的HashMap使用鏈表+紅黑樹解決哈希沖突問題(當(dāng)沖突節(jié)點較少時,使用鏈表,當(dāng)沖突節(jié)點較多時,使用紅黑樹)。
對于數(shù)據(jù)在內(nèi)存中的情況(如上述的TreeMap和HashMap),紅黑樹的表現(xiàn)是非常優(yōu)異的。但是對于數(shù)據(jù)在磁盤等輔助存儲設(shè)備中的情況(如MySQL等數(shù)據(jù)庫),紅黑樹并不擅長,因為紅黑樹長得還是太高了。當(dāng)數(shù)據(jù)在磁盤中時,磁盤IO會成為最大的性能瓶頸,設(shè)計的目標(biāo)應(yīng)該是盡量減少IO次數(shù);而樹的高度越高,增刪改查所需要的IO次數(shù)也越多,會嚴(yán)重影響性能。
B樹也稱B-樹(其中不是減號),是為磁盤等輔存設(shè)備設(shè)計的多路平衡查找樹,與二叉樹相比,樹的每個非葉節(jié)點可以有多個子樹。因此,當(dāng)總節(jié)點數(shù)量相同時,B樹的高度遠(yuǎn)遠(yuǎn)小于AVL樹和紅黑樹(B樹是一顆“矮胖子”),磁盤IO次數(shù)大大減少。
定義B樹最重要的概念是階數(shù)(Order),對于一顆m階B樹,需要滿足以下條件:
可以看出,B樹的定義,主要是對非葉結(jié)點的子節(jié)點數(shù)量和記錄數(shù)量的限制。
下圖是一個3階B樹的例子:
B樹的優(yōu)勢除了樹高小,還有對訪問局部性原理的利用。所謂局部性原理,是指當(dāng)一個數(shù)據(jù)被使用時,其附近的數(shù)據(jù)有較大概率在短時間內(nèi)被使用。B樹將鍵相近的數(shù)據(jù)存儲在同一個節(jié)點,當(dāng)訪問其中某個數(shù)據(jù)時,數(shù)據(jù)庫會將該整個節(jié)點讀到緩存中;當(dāng)它臨近的數(shù)據(jù)緊接著被訪問時,可以直接在緩存中讀取,無需進行磁盤IO;換句話說,B樹的緩存命中率更高。
B樹在數(shù)據(jù)庫中有一些應(yīng)用,如mongodb的索引使用了B樹結(jié)構(gòu)。但是在很多數(shù)據(jù)庫應(yīng)用中,使用了是B樹的變種B+樹。
B+樹也是多路平衡查找樹,其與B樹的區(qū)別主要在于:
由此,B+樹與B樹相比,有以下優(yōu)勢:
B+樹也存在劣勢:由于鍵會重復(fù)出現(xiàn),因此會占用更多的空間。但是與帶來的性能優(yōu)勢相比,空間劣勢往往可以接受,因此B+樹的在數(shù)據(jù)庫中的使用比B樹更加廣泛。
前面說到,B樹/B+樹與紅黑樹等二叉樹相比,最大的優(yōu)勢在于樹高更小。實際上,對于Innodb的B+索引來說,樹的高度一般在2-4層。下面來進行一些具體的估算。
樹的高度是由階數(shù)決定的,階數(shù)越大樹越矮;而階數(shù)的大小又取決于每個節(jié)點可以存儲多少條記錄。Innodb中每個節(jié)點使用一個頁(page),頁的大小為16KB,其中元數(shù)據(jù)只占大約128字節(jié)左右(包括文件管理頭信息、頁面頭信息等等),大多數(shù)空間都用來存儲數(shù)據(jù)。
對于一顆3層B+樹,第一層(根節(jié)點)有1個頁面,可以存儲1000條記錄;第二層有1000個頁面,可以存儲1000 * 1000條記錄;第三層(葉節(jié)點)有1000 * 1000個頁面,每個頁面可以存儲100條記錄,因此可以存儲1000 * 1000 * 100條記錄,即1億條。而對于二叉樹,存儲1億條記錄則需要26層左右。
最后,總結(jié)一下各種樹解決的問題以及面臨的新問題:
以上就是MySQL用B+樹作為索引結(jié)構(gòu)有什么好處的詳細(xì)內(nèi)容,更多關(guān)于MySQL B+樹索引結(jié)構(gòu)的資料請關(guān)注腳本之家其它相關(guān)文章!
標(biāo)簽:隨州 荊州 甘南 吉林 錦州 滄州 黑河 資陽
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL用B+樹作為索引結(jié)構(gòu)有什么好處》,本文關(guān)鍵詞 MySQL,用,樹,作為,索引,結(jié)構(gòu),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。