索引是在存儲引擎層實現的,而不是在服務器層實現的,所以不同存儲引擎具有不同的索引類型和實現。
索引能夠輕易將查詢性能提升幾個數量級。
對於非常小的表、大部分情況下簡單的全表掃描比建立索引更高效。對於中到大型的表,索引就非常有效。但是對於特大型的表,建立和使用索引的代價將會隨之增長。這種情況下,需要用到一種技術可以直接區分出需要查詢的一組數據,而不是一條記錄一條記錄地匹配,例如可以使用分區技術。
索引分類
1. B+Tree 索引
B+Tree 索引是大多數 MySQL 存儲引擎的默認索引類型。
因爲不再需要進行全表掃描,只需要對樹進行搜索即可,因此查找速度快很多。
可以指定多個列作爲索引列,多個索引列共同組成鍵。B+Tree 索引適用於全鍵值、鍵值範圍和鍵前綴查找,其中鍵前綴查找只適用於最左前綴查找。
除了用於查找,還可以用於排序和分組。
如果不是按照索引列的順序進行查找,則無法使用索引。
2. 哈希索引
基於哈希表實現,優點是查找非常快。
在 MySQL 中只有 Memory 引擎顯式支持哈希索引。
InnoDB 引擎有一個特殊的功能叫“自適應哈希索引”,當某個索引值被使用的非常頻繁時,會在 B+Tree 索引之上再創建一個哈希索引,這樣就讓 B+Tree 索引具有哈希索引的一些優點,比如快速的哈希查找。
限制:哈希索引只包含哈希值和行指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行。不過,訪問內存中的行的速度很快,所以大部分情況下這一點對性能影響並不明顯;無法用於分組與排序;只支持精確查找,無法用於部分查找和範圍查找;如果哈希衝突很多,查找速度會變得很慢。
3. 空間索引(R-Tree)
MyISAM 存儲引擎支持空間索引,可以用於地理數據存儲。
空間索引會從所有維度來索引數據,可以有效地使用任意維度來進行組合查詢。
4. 全文索引
MyISAM 存儲引擎支持全文索引,用於查找文本中的關鍵詞,而不是直接比較索引中的值。
使用 MATCH AGAINST,而不是普通的 WHERE。
索引的優點
-
大大減少了服務器需要掃描的數據量;
-
幫助服務器避免進行排序和創建臨時表;
-
將隨機 I/O 變爲順序 I/O。
索引優化
1. 獨立的列
在進行查詢時,索引列不能是表達式的一部分,也不能是函數的參數,否則無法使用索引。
例如下面的查詢不能使用 actor_id 列的索引:
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
2. 前綴索引
對於 BLOB、TEXT 和 VARCHAR 類型的列,必須使用前綴索引,只索引開始的部分字符。
對於前綴長度的選取需要根據 索引選擇性 來確定:不重複的索引值和記錄總數的比值。選擇性越高,查詢效率也越高。最大值爲 1,此時每個記錄都有唯一的索引與其對應。
3. 多列索引
在需要使用多個列作爲條件進行查詢時,使用多列索引比使用多個單列索引性能更好。例如下面的語句中,最好把 actor_id 和 film_id 設置爲多列索引。
SELECT film_id, actor_ id FROM sakila.film_actor
WhERE actor_id = 1 AND film_id = 1;
4. 索引列的順序
讓選擇性最強的索引列放在前面,例如下面顯示的結果中 customer_id 的選擇性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049
5. 聚簇索引
聚簇索引並不是一種索引類型,而是一種數據存儲方式。
術語“聚簇”表示數據行和相鄰的鍵值緊密地存儲在一起,InnoDB 的聚簇索引的數據行存放在 B+Tree 的葉子頁中。
因爲無法把數據行存放在兩個不同的地方,所以一個表只能有一個聚簇索引。
優點
-
可以把相關數據保存在一起,減少 I/O 操作;
-
因爲數據保存在 B+Tree 中,因此數據訪問更快。
缺點
-
聚簇索引最大限度提高了 I/O 密集型應用的性能,但是如果數據全部放在內存,就沒必要用聚簇索引。
-
插入速度嚴重依賴於插入順序,按主鍵的順序插入是最快的。
-
更新操作代價很高,因爲每個被更新的行都會移動到新的位置。
-
當插入到某個已滿的頁中,存儲引擎會將該頁分裂成兩個頁面來容納該行,頁分裂會導致表佔用更多的磁盤空間。
-
如果行比較稀疏,或者由於頁分裂導致數據存儲不連續時,聚簇索引可能導致全表掃描速度變慢。
6. 覆蓋索引
索引包含所有需要查詢的字段的值。
優點
-
因爲索引條目通常遠小於數據行的大小,所以若只讀取索引,能大大減少數據訪問量。
-
一些存儲引擎(例如 MyISAM)在內存中只緩存索引,而數據依賴於操作系統來緩存。因此,只訪問索引可以不使用系統調用(通常比較費時)。
-
對於 InnoDB 引擎,若二級索引能夠覆蓋查詢,則無需訪問聚簇索引。
爲什麼使用 B-Tree 和 B+Tree
紅黑樹等數據結構也可以用來實現索引,但是文件系統及數據庫系統普遍採用 B-/+Tree 作爲索引結構。
頁是計算機管理存儲器的邏輯塊,硬件及操作系統往往將主存和磁盤存儲區分割爲連續的大小相等的塊,每個存儲塊稱爲一頁(在許多操作系統中,頁的大小通常爲 4k),主存和磁盤以頁爲單位交換數據。
一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。爲了減少磁盤 I/O,磁盤往往不是嚴格按需讀取,而是每次都會預讀。這樣做的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每個節點只需要一次 I/O 就可以完全載入。B-Tree 中一次檢索最多需要 h-1 次 I/O(根節點常駐內存),漸進複雜度爲 O(h)=O(logdN)。一般實際應用中,出度 d 是非常大的數字,通常超過 100,因此 h 非常小(通常不超過 3)。而紅黑樹這種結構,h 明顯要深的多。並且於邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,效率明顯比 B-Tree 差很多。
B+Tree 更適合外存索引,原因和內節點出度 d 有關。由於 B+Tree 內節點去掉了 data 域,因此可以擁有更大的出度,擁有更好的性能。