轉自:http://blog.csdn.net/u013235478/article/details/50625677mysql
B+樹索引是B+樹在數據庫中的一種實現,是最多見也是數據庫中使用最爲頻繁的一種索引。B+樹中的B表明平衡(balance),而不是二叉(binary),由於B+樹是從最先的平衡二叉樹演化而來的。在講B+樹以前必須先了解二叉查找樹、平衡二叉樹(AVLTree)和平衡多路查找樹(B-Tree),B+樹即由這些樹逐步優化而來。sql
二叉樹具備如下性質:左子樹的鍵值小於根的鍵值,右子樹的鍵值大於根的鍵值。
以下圖所示就是一棵二叉查找樹,
對該二叉樹的節點進行查找發現深度爲1的節點的查找次數爲1,深度爲2的查找次數爲2,深度爲n的節點的查找次數爲n,所以其平均查找次數爲 (1+2+2+3+3+3) / 6 = 2.3次數據庫
二叉查找樹能夠任意地構造,一樣是2,3,5,6,7,8這六個數字,也能夠按照下圖的方式來構造:
可是這棵二叉樹的查詢效率就低了。所以若想二叉樹的查詢效率儘量高,須要這棵二叉樹是平衡的,從而引出新的定義——平衡二叉樹,或稱AVL樹。優化
平衡二叉樹(AVL樹)在符合二叉查找樹的條件下,還知足任何節點的兩個子樹的高度最大差爲1。下面的兩張圖片,左邊是AVL樹,它的任何節點的兩個子樹的高度差<=1;右邊的不是AVL樹,其根節點的左子樹高度爲3,而右子樹高度爲1;
spa
若是在AVL樹中進行插入或刪除節點,可能致使AVL樹失去平衡,這種失去平衡的二叉樹能夠歸納爲四種姿態:LL(左左)、RR(右右)、LR(左右)、RL(右左)。它們的示意圖以下:
.net
這四種失去平衡的姿態都有各自的定義:
LL:LeftLeft,也稱「左左」。插入或刪除一個節點後,根節點的左孩子(Left Child)的左孩子(Left Child)還有非空節點,致使根節點的左子樹高度比右子樹高度高2,AVL樹失去平衡。設計
RR:RightRight,也稱「右右」。插入或刪除一個節點後,根節點的右孩子(Right Child)的右孩子(Right Child)還有非空節點,致使根節點的右子樹高度比左子樹高度高2,AVL樹失去平衡。3d
LR:LeftRight,也稱「左右」。插入或刪除一個節點後,根節點的左孩子(Left Child)的右孩子(Right Child)還有非空節點,致使根節點的左子樹高度比右子樹高度高2,AVL樹失去平衡。指針
RL:RightLeft,也稱「右左」。插入或刪除一個節點後,根節點的右孩子(Right Child)的左孩子(Left Child)還有非空節點,致使根節點的右子樹高度比左子樹高度高2,AVL樹失去平衡。blog
AVL樹失去平衡以後,能夠經過旋轉使其恢復平衡。下面分別介紹四種失去平衡的狀況下對應的旋轉方法。
LL的旋轉。LL失去平衡的狀況下,能夠經過一次旋轉讓AVL樹恢復平衡。步驟以下:
LL旋轉示意圖以下:
RR的旋轉:RR失去平衡的狀況下,旋轉方法與LL旋轉對稱,步驟以下:
RR旋轉示意圖以下:
LR的旋轉:LR失去平衡的狀況下,須要進行兩次旋轉,步驟以下:
LR的旋轉示意圖以下:
RL的旋轉:RL失去平衡的狀況下也須要進行兩次旋轉,旋轉方法與LR旋轉對稱,步驟以下:
RL的旋轉示意圖以下:
B-Tree是爲磁盤等外存儲設備設計的一種平衡查找樹。所以在講B-Tree以前先了解下磁盤的相關知識。
系統從磁盤讀取數據到內存時是以磁盤塊(block)爲基本單位的,位於同一個磁盤塊中的數據會被一次性讀取出來,而不是須要什麼取什麼。
InnoDB存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小單位。InnoDB存儲引擎中默認每一個頁的大小爲16KB,可經過參數innodb_page_size將頁的大小設置爲4K、8K、16K,在MySQL中可經過以下命令查看頁的大小:
而系統一個磁盤塊的存儲空間每每沒有這麼大,所以InnoDB每次申請磁盤空間時都會是若干地址連續磁盤塊來達到頁的大小16KB。InnoDB在把磁盤數據讀入到磁盤時會以頁爲基本單位,在查詢數據時若是一個頁中的每條數據都能有助於定位數據記錄的位置,這將會減小磁盤I/O次數,提升查詢效率。
B-Tree結構的數據可讓系統高效的找到數據所在的磁盤塊。爲了描述B-Tree,首先定義一條記錄爲一個二元組[key, data] ,key爲記錄的鍵值,對應表中的主鍵值,data爲一行記錄中除主鍵外的數據。對於不一樣的記錄,key值互不相同。
一棵m階的B-Tree有以下特性:
1. 每一個節點最多有m個孩子。
2. 除了根節點和葉子節點外,其它每一個節點至少有Ceil(m/2)個孩子。
3. 若根節點不是葉子節點,則至少有2個孩子
4. 全部葉子節點都在同一層,且不包含其它關鍵字信息
5. 每一個非終端節點包含n個關鍵字信息(P0,P1,…Pn, k1,…kn)
6. 關鍵字的個數n知足:ceil(m/2)-1 <= n <= m-1
7. ki(i=1,…n)爲關鍵字,且關鍵字升序排序。
8. Pi(i=1,…n)爲指向子樹根節點的指針。P(i-1)指向的子樹的全部節點關鍵字均小於ki,但都大於k(i-1)
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相對於AVLTree縮減了節點個數,使每次磁盤I/O取到內存的數據都發揮了做用,從而提升了查詢效率。
B+Tree是在B-Tree基礎上的一種優化,使其更適合實現外存儲索引結構,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上有兩個頭指針,一個指向根節點,另外一個指向關鍵字最小的葉子節點,並且全部葉子節點(即數據節點)之間是一種鏈式環結構。所以能夠對B+Tree進行兩種查找運算:一種是對於主鍵的範圍查找和分頁查找,另外一種是從根節點開始,進行隨機查找。
可能上面例子中只有22條數據記錄,看不出B+Tree的優勢,下面作一個推算:
InnoDB存儲引擎中頁的大小爲16KB,通常表的主鍵類型爲INT(佔用4個字節)或BIGINT(佔用8個字節),指針類型也通常爲4或8個字節,也就是說一個頁(B+Tree中的一個節點)中大概存儲16KB/(8B+8B)=1K個鍵值(由於是估值,爲方便計算,這裏的K取值爲〖10〗^3)。也就是說一個深度爲3的B+Tree索引能夠維護10^3 * 10^3 * 10^3 = 10億 條記錄。
實際狀況中每一個節點可能不能填充滿,所以在數據庫中,B+Tree的高度通常都在2~4層。mysql的InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多隻須要1~3次磁盤I/O操做。
數據庫中的B+Tree索引能夠分爲彙集索引(clustered index)和輔助索引(secondary index)。上面的B+Tree示例圖在數據庫中的實現即爲彙集索引,彙集索引的B+Tree中的葉子節點存放的是整張表的行記錄數據。輔助索引與彙集索引的區別在於輔助索引的葉子節點並不包含行記錄的所有數據,而是存儲相應行數據的彙集索引鍵,即主鍵。當經過輔助索引來查詢數據時,InnoDB存儲引擎會遍歷輔助索引找到主鍵,而後再經過主鍵在彙集索引中找到完整的行記錄數據。