MySQL哈希索引


Hash索引

在MySQL中,索引是存儲在引擎層而不是服務器層實現的,所以,並沒有統一的索引標准。即使多個存儲引擎支持同一種類型的索引,那么他們的實現原理也是不同的。Innodb和MyISAM默認的索引是Btree索引;而Mermory默認的索引是Hash索引。

 

索引 / 存儲引擎

MyISAM

InnoDB

Memory

B-Tree索引

支持

支持

支持

HASH索引

不支持

不支持

支持

R-Tree索引

支持

支持

不支持

Full-text索引

支持

支持

不支持

最常用的索引也就是B-tree索引和Hash索引,且只有 MemoryNDB兩種引擎支持 Hash索引
 
哈希索引是基於哈希表實現,只有精確匹配索引所有列的查詢才有效,對於每一行數據,存儲引擎都會對所有的索引列計算一個哈希碼(hash code), 哈希碼是一個較小的值,大部分情況下不同的鍵值的行計算出來的哈希碼是不同的,但是也會有例外,就是說不同列值計算出來的hash值一樣的(即所謂的hash沖突),哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每一個數據行的指針,hash很適合做索引,為某一列或幾列建立hash索引,就會利用這一列或幾列的值通過一定的算法計算出一個hash值,對應一行或幾行數據
 
哈希索引的示意圖則是這樣的:

 

 

(圖片源自網絡)
針對上圖的理解:
keys:代表創建索引的列值;
buckets: 就是計算出來的hash值和對應的數據的物理位置組成的hash表;
entries:就是代表具體的數據行;
創建hash索引后,會為每個鍵值通過特定的算法計算出一個哈希碼(hash code),需要注意的是不同的鍵值計算出來的hash值可能是相同的,例上圖上的 John Smith 和Sandra Dee算出來的hash值都是152,然后找到hash值為152在hash表中的存儲數據的物理位置,這個位置對應着兩條數據也(就是John Smith 521-1234 和Sandra Dee 521-9655),然后再次遍歷這兩條數據,找到需要的數據,這就解釋了為啥hash沖突嚴重了,
hash索引效率降低的原因。
 
hash索引檢索數據的過程(摘自網絡)
當我們為某一列或某幾列建立hash索引時(目前就只有MEMORY引擎顯式地支持這種索引),會在硬盤上生成類似如下的文件:
hash值 存儲地址
1db54bc745a1 77#45b5
4bca452157d4 76#4556,77#45cc…
hash值即為通過特定算法由指定列數據計算出來,存儲地址即為所在數據行存儲在硬盤上的地址(也有可能是其他存儲地址,其實MEMORY會將hash表導入內存)。
這樣,當我們進行WHERE age = 18 時,會將18通過相同的算法計算出一個hash值==>在hash表中找到對應的儲存地址==>根據存儲地址取得數據==>最后一步確定這行數據是否是需要查詢的數據。
所以, 每次查詢時都要遍歷hash表,直到找到對應的hash值,數據量大了之后,hash表也會變得龐大起來,性能下降,遍歷耗時增加;
hash索引的適用情況:
檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查找,只需一次哈希算法即可立刻定位到相應的位置,速度非常快,但是哈希索引只適合某些特定的場景,而一旦適合哈希索引,則它帶來的性能提升非常明顯,除了memory引擎外,NDB引擎也支持唯一哈希索引;
 
innodb引擎有一個特殊的功能叫做自適應哈希索引,當innodb注意到某些索引值被使用的非常頻繁時,它會在內存中基於btree索引之上再創建一個哈希索引,這樣就讓btree索引也具有哈希索引的一些優點,比如:快速的哈希查找, 這是一個全自動的,內部的行為,用戶無法控制或者配置,不過如果有必要,可以選擇關閉這個功能(innodb_adaptive_hash_index=OFF,默認為ON)。
我們也可以在模擬innodb的方式在不支持hash索引的存儲引擎上創建hash索引
思路很簡單,就是在B-Tree索引的基礎上創建一個偽哈希索引,這和真正的哈希索引不是一回事,因為還是使用B-Tree進行查找。但它是使用hash值而不是鍵本身進行索引查找,你需要做的是在查詢的WHERE子句中手動指定使用哈希函數

 
 
 
 
如何處理哈希沖突

哈希沖突見另外一篇文章
 
正是因為hash表在處理較小數據量時具有無可比擬的優勢,所以hash索引很適合做緩存(內存數據庫)。如mysql數據庫的內存版本Memsql,使用量很廣泛的緩存工具Mencached,NoSql數據庫redis等,都使用了hash索引這種形式。當然,不想學習這些東西的話Mysql的MEMORY引擎也是可以滿足這種需求的。
 
簡單地說,哈希索引就是采用一定的哈希算法,把鍵值換算成新的哈希值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查找,只需一次哈希算法即可立刻定位到相應的位置,速度非常快。
從上面的圖來看,B+樹索引和哈希索引的明顯區別是:
    • 如果是等值查詢,那么哈希索引明顯有絕對優勢,因為只需要經過一次算法即可找到相應的鍵值;當然了,這個前提是,鍵值都是唯一的。如果鍵值不是唯一的,就需要先找到該鍵所在位置,然后再根據鏈表往后掃描,直到找到相應的數據;
    • 從示意圖中也能看到,如果是范圍查詢檢索,這時候哈希索引就毫無用武之地了,由於 Hash 索引比較的是進行 Hash 運算之后的 Hash 值,所以它只能用於等值的過濾,不能用於基於范圍的過濾,因為經過相應的 Hash 算法處理之后的 Hash 值的大小關系,並不能保證和Hash運算前完全一樣。 
    • 同理,哈希索引也沒辦法利用索引完成排序,以及like ‘xxx%’ 這樣的部分模糊查詢(這種部分模糊查詢,其實本質上也是范圍查詢);
    • 哈希索引也不支持多列聯合索引的最左匹配規則;、
      聯合索引中,Hash索引不能利用部分索引鍵查詢。
      對於聯合索引中的多個列,Hash是要么全部使用,要么全部不使用,並不支持BTree支持的聯合索引的最優前綴,也就是聯合索引的前面一個或幾個索引鍵進行查詢時,Hash索引無法被利用。
    • Hash 索引遇到大量Hash值相等的情況后性能並不一定就會比B-Tree索引高。 在有大量重復鍵值情況下,哈希索引的效率也是極低的,因為存在所謂的哈希碰撞問題
    • Hash索引任何時候都不能避免表掃描
      Hash索引是將索引鍵通過Hash運算之后,將Hash運算結果的Hash值和所對應的行指針信息存放於一個Hash表中,由於不同索引鍵存在相同Hash值,所以即使滿足某個Hash鍵值的數據的記錄條數,也無法從Hash索引中直接完成查詢,哈希索引只包含哈希值和行指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行(即不能使用哈希索引來做覆蓋索引掃描),還是要通過訪問表中的實際數據進行比較,並得到相應的結果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM