1、B-Tree索引的底層結構
- 全部的值都是按順序存儲的,而且每個葉子頁到根的距離相同,如圖所示,B-Tree索引的底層數據結構通常是B+樹,反應了MyISAM索引是如何工做的。
2、B-Tree索引的使用規則
B-Tree索引適用於全鍵值、鍵值範圍和鍵前綴查找,其中鍵前綴查找只適用於根據最左前綴查找。B-Tree索引支持的查詢原則以下所示:
- 全值匹配:全值匹配指的是和索引中的全部列進行匹配。
- 匹配最左前綴:前邊提到的索引能夠用於查找全部姓Allen的人,即只使用索引中的第一列。
- 匹配列前綴:也能夠只匹配某一列的值的開頭部分。例如前面提到的索引可用於查找全部以J開頭的姓的人。這裏也只用到了索引的第一列。
- 匹配範圍值:例如前邊提到的索引可用於查找姓在Allen和Barrymore之間的人。這裏也只使用了索引的第一列。
- 精確匹配某一列並範圍匹配另一列:前邊提到的索引也可用於查找全部姓爲Allen,而且名字是字母K開頭(好比Kim,Karl等)的人。即第一列last_name全匹配,第二列first_name範圍匹配。
由於索引樹的節點是有序的,因此除了按值查找以外,索引還能夠用於查詢中的ORDER BY操做(按順序查找),若是ORDER BY子句知足前面列出的幾種查詢類型,則這個索引也能夠知足對應的排序需求。
下面是一些關於B-Tree索引的限制:
- 若是不是按照索引的最左列開始查找,則沒法使用索引。例如上面例子中的索引沒法查找名字爲Bill的人,也沒法查找某個特定生日的日,由於這兩列都不是最左數據列。
- 若是查詢中有某個列的範圍查詢,則其右側全部列都沒法使用索引優化查找。
3、聚簇索引
聚簇索引並非一種單獨的索引類型,而是一種數據存儲方式。具體的細節依賴於其實現方式,可是InnoDB的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行。
當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁中,這也就是說數據行和相鄰的鍵值緊湊地存儲在一塊兒。
下圖展現了聚簇索引中的記錄是如何存放的。注意到,葉子頁包含了行的所有數據行,可是節點頁只包含了索引列。
|
聚簇索引可能對性能有幫助,但也可能致使嚴重的性能問題。
- 聚簇索引的優勢:
- 數據訪問更快,聚簇索引將索引和數據保存在同一個B-Tree中,所以從聚簇索引中獲取數據一般比在非聚簇索引中查找要快。
- 使用覆蓋索引掃描的查詢能夠直接使用頁節點中的主鍵值。
- 聚簇索引的缺點:
- 插入順序嚴重依賴插入順序。按照主鍵的順序插入是向InnoDB表中插入數據速度最快的方式,須要避免主鍵鍵值隨機的(不連續且值得分佈範圍很是大)聚簇索引,好比使用UUID做爲主鍵,而應該使用相似AUTO_INCREMENT的自增列。
- 更新聚簇索引列的代價很高,由於會強制InnoDB將每一個被更新的行移動位置到新的位置。
- 基於聚簇索引的表在插入新行,或者主鍵被更新致使須要移動行時,可能面臨「頁分裂」的問題。當行的主鍵值要求必須將這行插入到某個已滿的頁中時,存儲引擎會將該頁分裂成兩個頁面來容納該行,這就是一次頁分裂操做。頁分裂會致使表佔用更多的磁盤空間。
- 二級索引可能比想象的更大,由於在二級索引中的葉節點包含了引用行的主鍵列。
- 二級索引訪問須要兩次索引查找,而不是一次。
4、InnoDB和MyISAM引擎索引的差別
聚簇索引和非聚簇索引的數據分佈有區別,以及對應的主鍵索引和二級索引的數據分佈也有區別,一般會讓人感到困惑和意外。下圖展現了MyISAM和InnoDB的不一樣索引和數據存儲方式。
MyISAM的數據分佈很是簡單,按照數據插入的順序存儲在磁盤上,主鍵索引和二級索引的葉節點存儲着指針,指向對應的數據行。
InnoDB中,聚簇索引「就是」表,因此不會像MyISAM那樣須要獨立的行存儲。聚簇索引的每一個葉節點都包含了主鍵值和全部的剩餘列(在此例中是col2)。
InnoDB的二級索引和聚簇索引很不一樣。InnoDB二級索引的葉節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。
|
5、鬆散索引
MySQL並不支持鬆散索引掃描,也就是沒法按照不連續的方式掃描一個索引。一般,MySQL的索引掃描須要先定義一個起點和終點,即便須要的數據只是這段索引中不多數的幾個,MySQL仍然須要掃描這段索引中的每一個條目。
下面,咱們經過一個示例說明這點,假設咱們有以下索引(a,b),有下面的查詢:
|
由於索引的前導字段是列a,可是在查詢中只指定了字段b,MySQL沒法使用這個索引,從而只能經過全表掃描找到匹配的行,以下圖所示。
|
瞭解索引的物理結構的話,不難發現還能夠有一個更快的辦法執行上面的查詢。索引的物理結構(不是存儲引擎的API)是的能夠先掃描a列第一個值對應的b列的範圍,而後再跳到a列第二個不一樣值掃描對應的b列的範圍。下圖展現了若是由MySQL來實現這個過程會怎樣。
|
注意到,這時就無須再使用WHERE子句過濾,由於鬆散索引掃描已經跳過了全部不須要的記錄。
MySQL 5.0以後的版本,在某些特殊的場景下是可使用鬆散索引掃描的,例如,在一個分組查詢中須要找到分組的最大值和最小值:
|
在EXPLAIN中的Extra字段顯示"Using index for group-by",表示這裏將使用鬆散索引掃描。
6、覆蓋索引
索引除了是一種查找數據的高效方式以外,也是一種列數據的直接獲取方式。MySQL可使用索引來直接獲取列的數據,這樣就不須要讀取數據行。若是一個索引包含全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」。
覆蓋索引是很是有用的工具,可以極大地提升性能。SQL查詢只須要掃描索引而無需回表,會帶來不少好處:
- 索引條目數量和大小一般遠小於數據行的條目和大小,因此若是隻須要讀取索引,那麼MySQL就會極大地減小數據訪問量。
- 由於索引是按照列順序存儲的,因此對於I/O密集型的範圍查找會比隨機從磁盤讀取每一行數據的I/O要少的多。
- 因爲InnoDB的聚簇索引,覆蓋索引對InnoDB表特別有用。InnoDB的二級索引在葉子節點中保存了行的主鍵,索引若是二級主鍵可以覆蓋查詢,則避免對主鍵索引的第二次查詢。
當發起一個被覆蓋索引的查詢(也叫索引覆蓋查詢)時,在EXPLAIN的Extra列能夠看到"Using Index"的信息。例如,表sakila.inventory有一個多列索引(store_id, film_id)。MySQL若是隻須要訪問這兩列,就可使用這個索引作覆蓋索引,以下所示:
參考: