做了幾年後端開發,你真的瞭解B+樹索引嗎?

二叉樹(B-Tree)索引

如果沒有明確指定索引類型,那大家一般都是指二叉樹索引—通常使用二叉樹數據結構存儲索引數據。大多數的MySQL存儲引擎都支持這種索引類型。但Archive引擎是一個例外,直到MySQL 5.1版本後纔開始支持索引——通過自增列允許了單索引。

二叉樹只是一個泛稱,其實,不同的存儲引擎會使用不同的存儲結構。比如,NDB集羣存儲引擎使用了T-Tree數據結構,但是也稱爲BTREE,而InnoDB則使用了B+T樹。存儲引擎使用B-Tree的方式也各不相同,而這也會影響性能。例如,MyISAM使用了前置壓縮技術來縮小索引的存儲空間,而InnoDB並沒有做壓縮。MyISAM索引映射到數據行的物理存儲位置,而InnoDB則是通過數據行的主鍵進行映射的。雖然方式不同,但是原理是相似的。

二叉樹索引能夠加速數據訪問是因爲存儲引擎不需要在查找數據時做全表掃描。事實上,它首先在根節點查找。根節點的有一個區域存儲指向子節點的指針,存儲引擎根據這些指針查找數據。他在節點頁(定義了子節點的上下邊界值)中找到滿足條件的指針。最終,存儲引擎要麼是找不到對應的節點,要不就是找到正確的葉數據頁。


InnoDB的B+樹索引查找方式

葉數據頁是一個特殊的節點,這是因爲他們有指針指向索引對應的數據,而不是其他數據頁的指針。

由於二叉樹有序存儲了索引列,因此對於範圍搜索十分有用。例如,假設一個B+樹中的姓名索引是按照字母的降序排序的。那對於查找姓名中以字母K開頭將十分高效。假設有如下數據表:

CREATE TABLE people {
  last_name varchar(50) not null,
  first_name varchar(50)  not null,
  dob date  not null,
  gender enum(‘m’, ‘f’) not null,
  key(last_name, first_name, dob)
}

索引會包含last_namefirst_namedob三列的值。下圖展示了存儲的方式:

注意存儲引擎按照創建表的key中的字段次序依次建立索引,因此你會看到在最後面的Basinger Vivien (註上圖應該是Viven少了一個i)。由於last_namefirst_name都相同,因此存儲引擎會按照日期進行排序。

二叉樹在進行完整值匹配,範圍查找,匹配前面部分值的查找情況下表現很好。但只適用於索引最左匹配的情況下(即從where條件的從左到右進行索引匹配,如果遇到沒有索引或者索引失效的則右側即便有索引也是無效的)。以下是基於剛剛創建的表的例子:

  1. 完整值匹配:即從索引列檢索一個完整匹配的值,例如從Person表中查找姓名爲Cuba Allen,生日爲1960-01-01的人(即使用=查詢)。

  2. 最左匹配:查找last_name爲Allen的人,這隻會在條件是放置在where條件最左側的情況。

  3. 前綴匹配:可以查找匹配索引列前面部分值的數據。例如查找last_name以字母J開頭的人。

  4. 範圍查找:查找索引列處於一個區間的數據。例如查找last_name處於Allen和Barrymore之間的數據。

  5. 在一個列部分匹配而其他索引列精準匹配:例如查找last_name爲Allen而first_name以字母K開頭的數據。

  6. 索引查詢:直接對索引列的查詢,例如對索引列進行分組查詢。

由於二叉樹是有序排列的,因此對索引列按值查找或ORDER BY排序的效率都非常高。當然二叉樹索引也會有一些限制:

  1. 查詢條件不滿足最左匹配:例如查找一個last_name以某個字符結尾的人。或者查找姓名爲Bill或出生在指定日期的人。

  2. 不可以跳過索引包含的列:例如查詢last_name爲Smith且出生在指定日期的人。如果不指定first_name的值,那MySQL只會使用索引中第一列。

  3. 存儲引擎不能使用範圍搜索右側後的索引:例如如下WHERE語句:

WHERE last_name="Smith" AND first_name LIKE 'J%' AND dob='1976-12-23'

則只有last_namefirst_name會命中索引,而dob不會,這是因爲first_name使用的LIKE查詢屬於範圍查找,這會導致右側的dob索引失效。

這就是爲什麼索引列的次序十分重要的原因了,爲了優化查詢,也許你需要創建具有不同次序但是相同列的索引。當然,這在以後的MySQL版本中也可能會優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章