MySQL 索引與 B+ 樹
B+ 樹
MySQL Innodb 存儲引擎是使用 B+ 樹來組織索引的。在介紹 B+ 樹以前,先認識一下什么是 B 樹,B 樹是平衡二叉樹,與一般的二叉查找樹不同,平衡二叉樹首先滿足二叉查找樹的定義(左子樹的鍵小於根的鍵,右子樹的鍵大於根的鍵),其次必須滿足任何節點的兩個子樹的高度最大差為 1。B 樹的維護要求插入和更新節點時,通過 1 次或多次左旋和右旋來滿足平衡的條件。二叉查找樹是否平衡直接影響了查找需要比較的次數。
B+ 樹與普通的二叉樹不同,它的節點由多個關鍵字和指向子樹的指針組成,指向子樹的指針個數等於關鍵字個數加 1,這些子樹中關鍵字的范圍由它的父節點限定,真正的數據部分是存放在葉子節點中的。MySQL 中頁數據結構就是這些葉子節點,每個葉子節點對應了一個 Page,而 Page 的數據結構中有 PAGE_PREV 和 PAGE_NEXT 兩個指針,因此這些葉子節點兩兩之間也是相互連接的。
B+ 樹的插入操作
由於 B+ 樹需要在插入后依然保證平衡,因此插入操作會涉及到頁的拆分操作。Index Page 指的是非葉子結點而 Leaf Page 指的是葉子節點。插入操作分為以下三種情況:
- 當 Index Page 和 Leaf Page 都不滿時,直接將記錄插入到葉子節點中。
- 當 Index Page 不滿,Leaf Page 滿時,先將節點放入對應 Page,以中間節點作為依據,將 Page 拆分,然后將中間節點放入 Index Page 中,拆分后的左右記錄分別放在中間節點的左右兩邊。
- 當 Index Page 和 Leaf Page 都滿了,先拆分 Leaf Page,然后再拆分 Index Page,拆分 Index Page 的方法與拆分 Leaf Page 的方法一樣。
此處需要注意:為了在可能地情況下減少頁的拆分操作,B+ 樹提供了類似二叉平衡樹的旋轉操作。旋轉操作發生在 Leaf Page 已經滿,但是其左右兄弟節點沒有滿的情況下。
B+ 樹的刪除操作
B+ 樹使用填充因子來控制樹的變化,即中間節點關鍵字的數量和葉子節點關鍵字的數量和最大值的比例關系。此處以填充因子為 50% 為例。小於填充因子即為小於總容量的一半。刪除操作可以分為以下三種情況:
- 當 Leaf Page 關鍵字個數和 Index Page 節點關鍵字的個數都不小於填充因子時,直接將記錄從 Leaf Node 中刪除,如果該節點為 Index Page 節點,那么將 Index Page 節點替換為其右節點。
- 當 Leaf Page 關鍵字個數小於而 Index Page 不小於填充因子時,將 Leaf Page 節點和其兄弟節點合並,同時更新 Index Page 節點。
- 當 Leaf Page 關鍵字個數和 Index Page 節點關鍵字的個數都小於填充因子時,在情況 2 的基礎上還需要合並 Index Page 節點。
索引
索引在 MySQL 中就是使用 B+ 樹實現的,不同索引之間形成的 B+ 樹也是不同的。
聚集索引
聚集索引就是根據主鍵來構造 B+ 樹,葉子節點存放對應頁的行記錄。
輔助索引(非聚集索引)
輔助索引就是使用非主鍵構造的 B+ 樹,葉子節點存放的是對應的鍵值以及相應的聚集索引鍵。通過輔助索引來搜索一般是兩級的,第一級找到鍵值對應的聚集索引鍵,第二級是根據聚集索引鍵尋找行記錄。
聯合索引
聯合索引就是對表上的多個列進行索引,這樣構造的 B+ 樹的 Index Node 和 Page Node 包含多個鍵。
索引覆蓋
在聯合索引的情況去搜索行記錄,假設需要的行記錄的列正好包含在聯合索引中,那么此時結果將可以直接從聯合索引中得到,省去了從聚集索引中搜索,由於不包含整行的記錄所以可以大大減少 IO。
不使用索引的情況
當查詢滿足條件的行的所有列時,MySQL 不使用輔助索引,而是直接使用聚集索引。原因是即使使用了輔助索引,還是必須通過葉子節點中的目錄進行聚集索引的查找,才能得到完整的信息,那么直接從聚集索引中獲取即可。