1. 程式人生 > >MySQL技術內幕 InnoDB儲存引擎:B+樹索引

MySQL技術內幕 InnoDB儲存引擎:B+樹索引

B+ 樹索引並不能找到一個給定鍵值的具體行。 B+ 樹索引能找到的只是被查詢資料所在的頁。 然後資料庫通過把頁讀入到記憶體, 再在記憶體中進行查詢, 最後得到要查詢的資料。

平衡二叉樹

平衡二叉樹的定義如下:首先符合二叉查詢樹的定義,其次必須滿足任何節點的兩個字數的高度最大差為1。最好的想能需要建立一顆最優二叉樹,但是最優二叉樹的建立和維護需要大量的操作,因此,使用者一般只需要建立一顆平衡二叉樹即可。

平衡二叉樹的查詢速度很快,但是維護一顆平衡二叉樹的代價是非常大的。通常來說,需要1次或多次左旋和右旋來得到插入或更新後樹的平衡性。因此對於一顆平衡二叉樹的維護是有一定開銷的,不過平衡二叉樹多用於記憶體結構物件中,因此維護的開銷相對較小。

B+樹

B+樹的定義十分複雜,因此只簡要地介紹B+樹:B+樹是為磁碟或其他直接存取輔助裝置而設計的一種平衡查詢樹,在B+樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉節點中,各葉節點指標進行連線。

1、B+樹的插入操作

B+樹的插入必須保證插入後葉節點中的記錄依然排序,同時需要考慮插入B+樹的三種情況,每種情況都可能會導致不同的插入演算法,如表5-1所示。
在這裡插入圖片描述

可以看到,不管怎麼變化,B+樹總是會保持平衡。但是為了保持平衡,對於新插入的鍵值可能需要做大量的拆分頁(split)操作,而B+樹主要用於磁碟,因此頁的拆分意味著磁碟的操作,應該在可能的情況下儘量減少頁的拆分。因此,B+樹提供了旋轉(rotation)的功能。

旋轉發生在Leaf Page已經滿了、但是其左右兄弟節點沒有滿的情況下。這時B+樹並不會急於去做拆分頁的操作,而是將記錄移到所在頁的兄弟節點上。通常情況下,左兄弟被首先檢查用來做旋轉操作。

2、B+樹的刪除操作

B+樹使用填充因子(fill factor)來控制樹的刪除變化,50%是填充因子可設的最小值。B+樹的刪除操作同樣必須保證刪除後葉節點中的記錄依然排序,同插入一樣,B+樹的刪除操作同樣需要考慮如表5-2所示的三種情況,與插入不同的是,刪除根據填充因子的變化來衡量。
在這裡插入圖片描述

B+樹索引

前面討論的都是B+樹的資料結構及其一般操作,B+樹索引的本質就是B+樹在資料庫中的實現。但是B+索引在資料庫中有一個特點就是高扇出性,因此在資料庫中,B+樹的高度一般都在2-4層,也就是說查詢某一鍵值的行記錄時最多隻需要2-4次IO。因為當前一般的機械磁碟每秒至少可以做100次IO,2-4次的IO意味著查詢時間只需要0.02-0.04秒。

資料庫中的B+樹索引可以分為聚集索引和輔助索引(又被稱為非聚集索引),但不管是聚集還是輔助的索引,其內部都是B+樹的,即高度平衡的,葉子節點存放著所有的資料。聚集索引與輔助索引不同的是,葉子結點存放的是否是一整行的資訊。

1、聚集索引

InnoDB儲存引擎表是索引組織表,即表中資料按照主鍵順序存放。而聚簇索引就是按照每張表的主鍵構造一棵B+樹,同時葉子節點中存放的即為整張表的行記錄資料,也將聚集索引的葉子節點稱為資料頁。聚簇索引的這個特性決定了索引組織表中資料也是索引的一部分。同B+樹資料結構一樣,每個資料頁都通過一個雙向連結串列來進行連結。

由於實際的資料頁只能按照一棵B+樹進行排序,因此每張表只能擁有一個聚集索引。在多數情況下,查詢優化器傾向於採用聚集索引。因為聚集索引能夠在B+樹索引的葉子節點上直接找到資料。此外,由於定義了資料的邏輯順序,聚集索引能夠特別快地訪問針對範圍值的查詢。查詢優化器能夠快速發現某一段範圍的資料頁需要掃描。

許多文件會告訴我們:聚集索引按照順序物理地儲存資料,但是試想一下,如果聚集索引必須按照特定順序存放物理記錄,則維護成本顯得非常之高。所以,聚集索引的儲存並不是物理上連續的,而是邏輯上連續的。這其中有兩點:一是前面說過的頁通過雙向連結串列連結,頁按照主鍵的順序排序;另一點是每個頁中的記錄也是通過雙向連結串列進行維護的,物理儲存上可以同樣不按照主鍵儲存。

聚集索引的另一個好處是,它對於主鍵的排序查詢和範圍查詢速度非常快。葉子節點的資料就是使用者所要查詢的資料。

另一個是範圍查詢,即如果要查詢主鍵某一範圍內的資料,通過葉子節點的上層中間節點就可以得到頁的範圍,之後直接讀取資料頁即可。

2、輔助索引

對於輔助索引(secondary index,也稱非聚集索引),葉子節點並不包含行記錄的全部資料,葉子節點除了包含鍵值以外,每個葉子節點中的索引行中還包含了一個書籤(bookmark)。該書籤用來告訴Innodb儲存引擎哪裡可以找到與索引相對應的行資料。由於Innodb儲存引擎是索引組織表,因此Innodb儲存引擎的輔助索引的書籤就是相應行資料的聚集索引鍵。
在這裡插入圖片描述

輔助索引的存在並不影響資料在聚集索引中的組織,因此每張表可以由多個輔助索引。當通過輔助索引尋找資料時,InnoDB儲存引擎會遍歷輔助索引並通過葉級別的指標獲得指向主鍵索引的主鍵,然後再通過主鍵索引來找到一個完整的記錄。

舉例來說,如果在一棵高度為3的輔助索引樹中查詢資料,那需要對這棵輔助索引數查詢3次找到指定主鍵,如果聚集索引樹的高度同樣為3,那麼還需要對聚集索引樹進行3次查詢,最終找到一個完整的行資料所在的頁。因為一共需要6次邏輯IO訪問以得到最終的一個數據頁。

3、B+樹索引的分裂

B+樹索引頁的分裂並不總是從頁的中間記錄開始,這樣可能會導致頁空間的浪費。

InnoDB儲存引擎的Page Header中有以下幾個部分用來儲存插入的順序資訊:

  • PAGE_LAST_INSERT
  • PAGE_DIRECTION
  • PAGE_N_DIRECTION

通過這些資訊,InnoDB儲存引擎可以決定是向左還是向右進行分裂,同時決定將分裂點記錄為哪一個。若插入時隨機的,則取頁的中間記錄作為分裂點的記錄,這和之前介紹的相同。若往同一個方向進行插入的記錄數量為5,並且目前已經定位到記錄(InnoDB儲存引擎插入時,首先需要進行定位,定位到的記錄為待插入記錄的前一條記錄)之後還有3條記錄,則分裂點的記錄為定位到的記錄後的第三條記錄,否則分裂點記錄就是待插入的記錄。

4、B+樹索引的管理

查看錶中索引的資訊可以使用SHOW INDEX語句。

接著我們來具體講解每個列的含義:

  • Table:索引所在的表名。
  • Non_unique:非唯一的索引,可以看到primary key是0,因為必須是唯一的。
  • Key_name:索引的名稱,我們可以通過這個名稱來DROP INDEX。
  • Seq_in_index:索引中該列的位置,如果看聯合索引idx_a_b就比較直觀了。
  • Column_name:索引的列
  • Collation:列以什麼方式儲存在索引中。可以是’A’或者NULL。B+樹索引總是A,即排序的。如果使用了Heap儲存引擎,並且建立了Hash索引,這裡就會顯示NULL了。因為Hash根據Hash桶來存放索引資料,而不是對資料進行排序。
  • Cardinality:非常關鍵的值,表示索引中唯一值的數目的估計值。Cardinality/表的行數應儘可能接近1,如果非常小,那麼需要考慮是否還需要建這個索引。
  • Sub_part:是否是列的部分被索引。如果看idx_b這個索引,這裡顯示100,表示我們只索引b列的前100個字元。如果索引整個列,則該欄位為NULL。
  • Packed:關鍵字如何被壓縮。如果沒有被壓縮,則為NULL。
  • Null:是否索引的列含有NULL值。可以看到idx_b這裡為Yes。因為我們定義的b列允許NULL值。
  • Index_type:索引的型別。InnoDB儲存引擎只支援B+樹索引,所以這裡顯示的都是BTREE。
  • Comment:註釋。

Cardinality值非常關鍵,優化器會根據這個值來判斷是否使用這個索引。但是這個值並不是實時更新的,並非每次索引的更新都會更新該值,因為這樣代價太大了。因此這個值是不太準確的,只是一個大概的值。上面顯示的結果主鍵的Cardinality為2,但是很顯然我們表中有4條記錄,這個值應該是4。如果需要更新索引Cardinality的資訊,可以使用ANALYZE TABLE命令。如:

analyze table t;
show index from t;

這時的Cardinality的值就對了。不過,在每個系統上可能得到的結果不一樣,因為ANALYZE TABLE現在還存在一些問題,可能會影響得到最後的結果。

另一個問題是MySQL資料庫對於Cardinality計數的問題,在執行一段時間後,可能會看到下面的結果:

show index from Profile;

Cardinality為NULL,在某些情況下可能會發生索引建立了、但是沒有用到,或者explain兩條基本一樣的語句,但是最終出來的結果不一樣。一個使用索引,另外一個使用全表掃描,這時最好的解決辦法就是做一次ANALYZE TABLE的操作。因此我建議在一個非高峰時間,對應用程式下的幾張核心表做ANALYZE TABLE操作,這能使優化器和索引更好地為你工作。

本文整理自:《MySQL技術內幕 InnoDB儲存引擎
個人微信公眾號:
這裡寫圖片描述

作者:jiankunking 出處:http://blog.csdn.net/jiankunking