要回答好這個問題,首先咱們要弄懂什麼是索引?索引常見的數據結構有哪些?這些數據結構有何優缺點?只有弄懂這些,再去比較,纔會知道爲啥要用B+樹做爲MySQL數據庫的存儲索引了。html
MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。它的本質就是數據結構,單獨存儲在磁盤上,用它來提升數據查詢的效率。android
適合做爲索引的結構應該是儘量少的執行磁盤IO操做,由於執行磁盤IO操做很是的耗時。面試
採起二分查找的思想,O(log N)的複雜度就能夠完成對數據的查找任務,查找所需的最大次數等同於二叉查找樹的高度。算法
它具備如下特性:數據庫
以下圖所示:排序工具數據結構
對該二叉樹的節點進行查找發現深度爲1的節點的查找次數爲1,深度爲2的查找次數爲2,深度爲n的節點的查找次數爲n,所以其平均查找次數爲 (1+2+2+3+3+3) / 6 = 2.3次app
二叉查找樹能夠任意地構造,這樣會出現一種極端狀況,若是依次插入以下六個節點:7,6,5,4,那麼就會變成下圖所示:工具
這樣退化成線性表,致使樹高度太高,從而查詢效率就下降了。性能
那麼如何解決二叉查找樹屢次插入新節點而致使的不平衡?這裏就要引出新的定義——平衡二叉樹,或稱AVL樹。優化
樹的查找性能取決於樹的高度,讓樹儘量平衡,就是爲了下降樹的高度。
平衡二叉查找樹(AVL樹)在符合二叉查找樹的條件下,還知足任何節點的兩個子樹的高度最大差爲1。以下圖所示,它的任何節點的兩個子樹的高度差<=1。
若是在AVL樹中進行插入或刪除節點,可能致使AVL樹失去平衡。
紅黑樹是一種自平衡的二叉查找樹。除了符合二叉查找樹的基本特性外,它還具有如下特性:
節點是紅色或者黑色;
每一個葉子的節點都是黑色的空節點(NULL);
每一個紅色節點的兩個子節點都是黑色的;
從任意節點到其每一個葉子的全部路徑都包含相同的黑色節點。
紅黑樹相比於BST和AVL樹有什麼優勢?
紅黑樹是犧牲了嚴格的高度平衡的優越條件爲代價,它只要求部分地達到平衡要求,下降了對旋轉的要求,從而提升了性能。
紅黑樹可以以O(log2 n)的時間複雜度進行搜索、插入、刪除操做。此外,因爲它的設計,任何不平衡都會在三次旋轉以內解決。固然,還有一些更好的,但實現起來更復雜的數據結構可以作到一步旋轉以內達到平衡,但紅黑樹可以給咱們一個比較「便宜」的解決方案。
相比於BST,由於紅黑樹能夠能確保樹的最長路徑不大於兩倍的最短路徑的長度,因此能夠看出它的查找效果是有最低保證的。在最壞的狀況下也能夠保證O(logN)的,這是要好於二叉查找樹的。由於二叉查找樹最壞狀況可讓查找達到O(N)。
紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高,因此在插入和刪除中所作的後期維護操做確定會比紅黑樹要耗時好多,可是他們的查找效率都是O(logN),因此紅黑樹應用仍是高於AVL樹的。實際上插入,AVL 樹和紅黑樹的速度取決於你所插入的數據.若是你的數據分佈較好,則比較宜於採用 AVL樹(例如隨機產生系列數),可是若是你想處理比較雜亂的狀況,則紅黑樹是比較快的。
- AVL是嚴格平衡樹,所以在增長或者刪除節點的時候,根據不一樣狀況,旋轉的次數比紅黑樹要多;
- 而紅黑是弱平衡的,用非嚴格的平衡來換取增刪節點時候旋轉次數的下降;
- 因此簡單說,查找的次數遠遠大於插入和刪除,那麼選擇AVL樹;若是搜索、插入刪除次數幾乎差很少,應該選擇RB樹。
紅黑樹的應用
紅黑樹的各類操做的時間複雜度是O(lgn),邏輯上很近的節點(父子)物理上可能很遠,沒法利用局部性,IO次數多查找慢,效率低。
紅黑樹的高度雖然有必定的控制,而數據庫當中通常要把索引樹的高度控制在3-5層,這點紅黑樹顯然沒法作到。B-Tree是爲磁盤等外存儲設備設計的一種平衡查找樹,是一種多路平衡搜索樹,既然它是多路平衡的,那麼就不在像紅黑樹那樣只有2個子節點了,既然有多個子節點,樹的高度就能夠控制了,同時它也跟紅黑樹同樣,數據是排序的,能夠快速查找。
B樹具備如下特色:
下面是一顆B樹:
B-Tree結構的數據可讓系統高效的找到數據所在的磁盤塊。爲了描述B-Tree,首先定義一條記錄爲一個二元組[key, data] ,key爲記錄的鍵值,對應表中的主鍵值,data爲一行記錄中除主鍵外的數據。對於不一樣的記錄,key值互不相同。
B-Tree中的每一個節點根據實際狀況能夠包含大量的關鍵字信息和分支,以下圖所示爲一個3階的B-Tree:
每一個節點佔用一個盤塊的磁盤空間,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指針,指針存儲的是子節點所在磁盤塊的地址。兩個關鍵詞劃分紅的三個範圍域對應三個指針指向的子樹的數據的範圍域。以根節點爲例,關鍵字爲17和35,P1指針指向的子樹的數據範圍爲小於17,P2指針指向的子樹的數據範圍爲17~35,P3指針指向的子樹的數據範圍爲大於35。
模擬查找關鍵字29的過程:
分析上面過程,發現須要3次磁盤I/O操做,和3次內存查找操做。因爲內存中的關鍵字是一個有序表結構,能夠利用二分法查找提升效率。而3次磁盤I/O操做是影響整個B-Tree查找效率的決定因素。
B+Tree是在B-Tree(不要讀成B減樹,而是B樹)基礎上的一種優化,使其更適合實現外存儲索引結構,InnoDB存儲引擎就是用B+Tree實現其索引結構。
從上一節中的B-Tree結構圖中能夠看到每一個節點中不只包含數據的key值,還有data值。而每個頁的存儲空間是有限的,若是data數據較大時將會致使每一個節點(即一個頁)能存儲的key的數量很小,當存儲的數據量很大時一樣會致使B-Tree的深度較大,增大查詢時的磁盤I/O次數,進而影響查詢效率。在B+Tree中,全部數據記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只存儲key值信息,這樣能夠大大加大每一個節點存儲的key值數量,下降B+Tree的高度。
B+Tree相對於B-Tree有幾點不一樣:
將上一節中的B-Tree優化,因爲B+Tree的非葉子節點只存儲鍵值信息,假設每一個磁盤塊能存儲4個鍵值及指針信息,則變成B+Tree後其結構以下圖所示:
數據都在葉子節點上,而且增長了順序訪問指針,每一個葉子節點都指向相鄰的葉子節點的地址。相比B-Tree來講,進行範圍查找時只須要查找兩個節點,進行遍歷便可,提升了區間訪問性能(無需返回上層父節點重複遍歷查找減小IO操做)。而B-Tree須要獲取全部節點,相比之下B+Tree效率更高。
紅黑樹等數據結構也能夠用來實現索引,可是文件系統及數據庫系統廣泛採用B-/+Tree做爲索引結構。
通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程當中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,因此評價一個數據結構做爲索引的優劣最重要的指標就是在查找過程當中磁盤I/O操做次數的漸進複雜度。換句話說,索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數。
這樣咱們對比上面的B+樹和紅黑樹,好比查找節點21,紅黑樹要磁盤IO5次,而B+樹只要2次,也就是說磁盤IO次數大體爲樹的高度,這樣B+樹就脫穎而出了,成爲實現索引的不二選擇。
實際狀況中每一個節點可能不能填充滿,所以在數據庫中,B+Tree的高度通常都在2~4層。MySQL的InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多隻須要1~3次磁盤I/O操做。
數據庫中的B+Tree索引能夠分爲彙集索引(clustered index)和輔助索引(secondary index)。上面的B+Tree示例圖在數據庫中的實現即爲彙集索引,彙集索引的B+Tree中的葉子節點存放的是整張表的行記錄數據。輔助索引與彙集索引的區別在於輔助索引的葉子節點並不包含行記錄的所有數據,而是存儲相應行數據的彙集索引鍵,即主鍵。當經過輔助索引來查詢數據時,InnoDB存儲引擎會遍歷輔助索引找到主鍵,而後再經過主鍵在彙集索引中找到完整的行記錄數據。
聚簇索引(彙集索引):並非一種單獨的索引類型,而是一種數據存儲方式。具體細節取決於不一樣的實現,InnoDB的聚簇索引其實就是在同一個結構中保存了B-Tree索引(技術上來講是B+Tree)和數據行。
數據庫索引採用B+樹而不是B樹的主要緣由:B+樹只要遍歷葉子節點就能夠實現整棵樹的遍歷,並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹只能中序遍歷全部節點,效率過低。
文件與數據庫都是須要較大的存儲,也就是說,它們都不可能所有存儲在內存中,故須要存儲到磁盤上。而所謂索引,則爲了數據的快速定位與查找,那麼索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數,所以B+樹相比B樹更爲合適。數據庫系統巧妙利用了局部性原理與磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入,而紅黑樹這種結構,高度明顯要深的多,而且因爲邏輯上很近的節點(父子)物理上可能很遠,沒法利用局部性。最重要的是,B+樹還有一個最大的好處:方便掃庫。B樹必須用中序遍歷的方法按序掃庫,而B+樹直接從葉子結點挨個掃一遍就完了,B+樹支持range-query很是方便,而B樹不支持,這是數據庫選用B+樹的最主要緣由。
問:爲何索引結構默認使用B-Tree,而不是hash,二叉樹,紅黑樹?
hash:雖然能夠快速定位,可是沒有順序,IO複雜度高。
二叉樹:樹的高度不均勻,不能自平衡,查找效率跟數據有關(樹的高度),而且IO代價高。
紅黑樹:樹的高度隨着數據量增長而增長,IO代價高。
若是隻選一個數據,那確實是hash更快。可是數據庫中常常會選擇多條,這時候因爲B+樹索引有序,而且又有鏈表相連,它的查詢效率比hash就快不少了。
並且數據庫中的索引通常是在磁盤上,數據量大的狀況可能沒法一次裝入內存,B+樹的設計能夠容許數據分批加載,同時樹的高度較低,提升查找效率。
問:爲何官方建議使用自增加主鍵做爲索引。
結合B+Tree的特色,自增主鍵是連續的,在插入過程當中儘可能減小頁分裂,即便要進行頁分裂,也只會分裂不多一部分。而且能減小數據的移動,每次插入都是插入到最後。總之就是減小分裂和移動的頻率。
參考:
https://blog.csdn.net/majiawenzzz/article/details/81098870
https://blog.csdn.net/UFO___/article/details/80522453
https://www.cnblogs.com/liqiangchn/p/9060521.html
https://www.jianshu.com/p/486a514b0ded
https://blog.csdn.net/ifollowrivers/article/details/73614549