索引是存儲引擎用來快速查找記錄的一種數據結構,按照實現的方式有不同的種類,想B-Tree索引,hash索引,空間數據索引和全文索引等。下面主要說一下B-Tree索引和Hash索引。
人們在談論索引的時候如果沒有特別說明,一般指的是B-Tree索引。B-Tree索引是使用B-Tree數據結構來存儲索引的。B-Tree通常意味着所有的值是按照順序存儲的。B-Tree樹有如下幾個特征:
⑴樹中每個結點至多有m 棵子樹;
⑵若根結點不是葉子結點,則至少有兩棵子樹;
⑶除根結點之外的所有非終端結點至少有[m/2] 棵子樹;
⑷所有的非終端結點中包含以下信息數據:
(n,A0,K1,A1,K2,…,Kn,An)
其中:Ki(i=1,2,…,n)為關鍵碼,且Ki<Ki+1,
Ai 為指向子樹根結點的指針(i=0,1,…,n),且指針Ai-1 所指子樹中所有結點的關鍵碼均小於Ki (i=1,2,…,n),An 所指子樹中所有結點的關鍵碼均大於Kn.
n 為關鍵碼的個數。
⑸所有的葉子結點都出現在同一層次上,並且不帶信息(可以看作是外部結點或查找失敗的結點,實際上這些結點不存在,指向這些結點的指針為空)。 即所有葉節點具有相同的深度,等於樹高度。
對於B-Tree索引,存儲引擎在查找記錄的時候不再是通過掃描全表來獲取需要的數據么人是從索引的根節點進行搜索,根節點的槽中存放了指向子節點的指針,存儲引擎根據這些指針向下查找。最后找到葉子節點。葉子節點比較特殊,他的指針指向的是索引的數據。而且不同的存儲引擎實現也不一樣。在mysql中MyISAM中指針指向的是數據的指針,而在InnoDB中,直接存放的是數據。
那么在什么情況下B-Tree可以使用索引呢?假設存在一張user表,我們對(“姓”,“名”,“出生日期”)建立符合索引,那么在B-Tree的key中就是按照姓,名,出生日期順序存儲的,這樣方便我們理解下面這些情況:
1,全值匹配,指的是索引中的所有列進行匹配。比如 where 姓=‘張’ and 名=‘小凡’and 出生年月=‘1990-12-21’;
2,匹配最左前綴。比如查找 姓=‘張’,只能使用第一列。你想如果我們要名=‘三’,那么可以是‘趙三’,‘李三’索引中先根據姓排序,所以名=‘三’的根本沒在一起,無法使用索引。
3,匹配列前綴 比如查找‘歐%’;
4,匹配范圍值 可以使用索引列的第一列。
5,精確匹配某一列,在范圍匹配另外一列。
根據B-Tree存儲的特點,很容易知道有一些情況是沒有辦法使用索引的。比如如下情況:
1,不是按照索引列最左側開始查找。
2,跳過中介的列。
3,如果某個列采用范圍查找,那么右側的列都沒有辦法使用索引。
B-Tree索引有一些獨特的好處的,比如在B-Tree樹的key中存放了索引列的值,所以如果我們只需要查詢索引列的結果就可以直接使用索引而不需要去查找記錄了。這就是覆蓋索引的優化。
另外,因為B-Tree索引是有序的,所以除了查找之外,索引還可以用於查詢中的order by 排序。
Hash索引
哈希索引是基於哈希表實現的。只有精確匹配索引所有列的的查詢才有效。他的實現是存儲殷勤會對每一行數據的索引列計算哈希碼,並將哈希碼和指向該記錄的指針維護起來,對於hash相同的,采用鏈表的方式解決沖突。類似於hashmap。因為索引的結構是十分緊湊的,所以hash索引的查詢很快。
但是hash索引也有他的限制:
1,hash索引只包含了哈希值和行指針,索引不能避免讀取行,不能使用覆蓋索引。
2,hash索引並不是按照索引順序存儲的,無法用於排序。
3,hash索引不支持部分或者區域查找,部分列的hash結果是不同的。
在Mysql中InnoDB引擎有一個特殊的功能叫做自適應哈希索引,他會在內存中基於B-Tree索引的基礎上面創建一個哈希索引,這讓B-Tree索引頁具備了一些哈希索引的優點。
在《高性能的Mysql》這本樹中,作者舉了一個自定義哈希索引的列子。假設我們在一個表中大量存儲了URL,而且需要根據URL來進行查找。因為URL比較長,這個時候如果我們使用B-Tree索引來,索引會非常的大。解決的辦法是在列表中增加一個列,用來存放URL的哈希值,可以通過CRC32對URL進行計算,並存放在列表中,在查找的時候通過CRC32對url進行計算匹配列表中的hash值。
但是這個地方需要注意hash沖突的問題,所以在查詢的時候需要添加url的匹配。
例如:where url_crc=CRC32(“http://www.cnblogs.com/yimixiong/p/7400914.html”) and url=” http://www.cnblogs.com/yimixiong/p/7400914.html”