btree索引:
如果沒有特別指明類型,多半說的就是btree索引,它使用btree數據結構來存儲數據,大多數mysql引擎都支持這種索引,archive引擎是一個例外,5.1之前這個引擎不支持任何索引,5.1開始才支持單列自增的索引。innodb使用b+tree=btree(btree已經不使用了)
存儲引擎以不同的方式使用btree索引,性能也各不相同,各有優劣,如:myisam使用前綴壓縮技術使得索引更小(但也可能導致連接表查詢性能降低),但innodb則按照原數據格式進行存儲,再如:myisam索引通過數據的物理位置來引用被索引的行,而innodb則根據主鍵來引用被索引的行。
btree通常意味着所有的值都是按照順序存儲的,並且每一個葉子頁到根的距離相同
,下圖是innodb索引工作示意圖,myisam使用的結構有所不同,但基本思想類似:
圖片來源於高性能mysql第三版
btree索引能夠加快訪問數據的速度,因為存儲引擎不再需要進行全表掃描來獲取需要的數據,取而代之的是從索引的根節點開始進行搜索,根節點的槽中存放了指向子節點的指針,存儲引擎根據這些指針向下層查找,通過比較節點頁的值和要查找的值可以找到合適的指針進入下一層子節點,這些指針實際上定義了子節點頁中值的上限和下限,最終存儲引擎要么是找到對應的值,要么是該記錄不存在。
葉子節點比較特別,他們的指針指向的是被索引的數據,而不是其他的節點頁(不同的引擎指針類型不同),其實在根節點與葉子節點之間可能有很多層節點頁,樹的深度和表的大小直接相關。
btree樹索引列是順序組織存儲的,所以很適合查找范圍數據,如
有表:
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_name,first_name,dob列的值,下圖顯示了該索引是如何組織數據的存儲的:
圖片來源於高性能mysql第三版
注意:索引對多個值進行排序的依據是create table語句中定義索引時的列順序,上圖中,最后兩個值的姓名都一樣時,就按照出生日期來排序了。
可以使用btree索引的查詢類型,btree索引使用用於全鍵值、鍵值范圍、或者鍵前綴查找,其中鍵前綴查找只適合用於根據最左前綴的查找。前面示例中創建的多列索引對如下類型的查詢有效:
A:全值匹配
全值匹配指的是和索引中的所有列進行匹配,即可用於查找姓名和出生日期
B:匹配最左前綴
如:只查找姓,即只使用索引的第一列
C:匹配列前綴
也可以只匹配某一列值的開頭部分,如:匹配以J開頭的姓的人,這里也只是使用了索引的第一列,且是第一列的一部分
D:匹配范圍值
如查找姓在allen和barrymore之間的人,這里也只使用了索引的第一列
E:精確匹配某一列並范圍匹配另外一列
如查找所有姓為allen,並且名字字母是K開頭的,即,第一列last_name精確匹配,第二列first_name范圍匹配
F:只訪問索引的查詢
btree通常可以支持只訪問索引的查詢,即查詢只需要訪問索引,而無需訪問數據行,即,這個就是覆蓋索引的概念。需要訪問的數據直接從索引中取得。
因為索引樹中的節點是有序的,所以除了按值查找之外,索引還可以用於查詢中的order by操作,一般來說,如果btree可以按照某種方式查找的值,那么也可以按照這種方式用於排序,所以,如果order by子句滿足前面列出的幾種查詢類型,則這個索引也可以滿足對應的排序需求。
下面是關於btree索引的限制:
A:如果不是按照索引的最左列開始查找的,則無法使用索引(注意,這里不是指的where條件的順序,即where條件中,不管條件順序,只要where中出現的列在多列索引中能夠從最左開始連貫起來就能使用到多列索引)
B:不能跳過索引中的列,如:查詢條件為姓和出生日期,跳過了名字列,這樣,多列索引就只能使用到姓這一列
C:如果查詢中有某個列的范圍查詢,則其右邊所有列都無法使用索引優化查詢,如:where last_name=xxx and first_name like ‘xxx%’ and dob=’xxx’;這樣,first_name列可以使用索引,這列之后的dob列無法使用索引。
哈希索引:
基於哈希表實現,只有精確匹配索引所有列的查詢才有效,對於每一行數據,存儲引擎都會對所有的索引列的值計算一個哈希碼,哈希碼是一個較小的值,並且不同鍵值的行計算出來的哈希碼不一樣,哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。
在mysql中,只有memory引擎顯式支持哈希索引,這也是memory引擎表的默認索引類型,memory也支持btree,值得一提的是,memory引擎是支持非唯一哈希索引的。在數據庫世界里是比較與眾不同,如果多個列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希條目中。
示例:
每個槽的編號是順序的,但是數據行不是順序的。下面來看一句查詢:
select lname from testhash where fname='Peter';
mysql先計算Peter的哈希值,並使用該值尋找對應的記錄指針,因為f(‘Peter’)=8784,所以mysql在索引中查找8784,可以找到指向第三行的指針,最后一步是比較第三行的值是否為Peter,以確保就是要查找的行。因為索引自身只需要存儲對應的哈希值,所以索引的結構十分緊湊,這也讓哈希索引查找的速度非常快,然而,哈希索引也有限制,如下:
A:哈希索引只包含哈希值和行指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行(即不能使用哈希索引來做覆蓋索引掃描),不過,訪問內存中的行的速度很快(因為memory引擎的數據都保存在內存里),所以大部分情況下這一點對性能的影響並不明顯。
B:哈希索引數據並不是按照索引列的值順序存儲的,所以也就無法用於排序
C:哈希索引也不支持部分索引列匹配查找,因為哈希索引始終是使用索引的全部列值內容來計算哈希值的。如:數據列(a,b)上建立哈希索引,如果只查詢數據列a,則無法使用該索引。
D:哈希索引只支持等值比較查詢,如:=,in(),<=>(注意,<>和<=>是不同的操作),不支持任何范圍查詢(必須給定具體的where條件值來計算hash值,所以不支持范圍查詢)。
E:訪問哈希索引的數據非常快,除非有很多哈希沖突,當出現哈希沖突的時候,存儲引擎必須遍歷鏈表中所有的行指針,逐行進行比較,直到找到所有符合條件的行。
F:如果哈希沖突很多的話,一些索引維護操作的代價也很高,如:如果在某個選擇性很低的列上建立哈希索引(即很多重復值的列),那么當從表中刪除一行時,存儲引擎需要遍歷對應哈希值的鏈表中的每一行,找到並刪除對應的引用,沖突越多,代價越大。
從上面描述可知,哈希索引只適合某些特定的場景,而一旦適合哈希索引,則它帶來的性能提升非常明顯,除了memory引擎外,NDB引擎也支持唯一哈希索引,且NDB存儲引擎中作用非常特殊,但這里不討論。
innodb引擎有一個特殊的功能叫做自適應哈希索引,當innodb注意到某些索引值被使用的非常頻繁時,它會在內存中基於btree索引之上再創建一個哈希索引,這樣就讓btree索引也具有哈希索引的一些優點,比如:快速的哈希查找,這是一個全自動的,內部的行為,用戶無法控制或者配置,不過如果有必要,可以選擇關閉這個功能(innodb_adaptive_hash_index=OFF,默認為ON)。