「Mysql索引原理(三)」Mysql中的Hash索引原理

Hash索引

概念

? ? ? ?基于哈希表實現(xiàn),只有匹配所有列的查詢才有效。對于每一行數(shù)據(jù),存儲引擎都會對所有索引列計算一個哈希碼,哈希碼是一個較小的值,不同鍵值的行計算出的哈希碼也不一樣。哈希索引將所有的哈希碼存儲在索引中,同時保存指向每個數(shù)據(jù)行的指針。

? ? ? ?如果多個列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希條目中去。

舉例

CREATE TABLE `testhash` (
  `fname` varchar(50) DEFAULT NULL,
  `lname` varchar(50) DEFAULT NULL,
  KEY `fname` (`fname`) USING HASH
) ENGINE=MEMORY;

? ? ? ?為什么用MEMORY存儲引擎,因為mysql只有MEMORY存儲引擎顯示支持哈希索引。
? ? ? ?表包含數(shù)據(jù):select * from testhash

假設索引使用哈希函數(shù)f()來生成哈希碼:
? ? ? ?f('Arjen')=2323
? ? ? ?f('Baron')=7437
? ? ? ?f('Peter')=8784
? ? ? ?f('Vadim')=2458
則,哈希索引的數(shù)據(jù)結構是:

哈希表中哈希碼是順序的,導致對應的數(shù)據(jù)行是亂序的。

看如下查詢:

select lname from testhash where fname ='Peter'

? ? ? ?Mysql首先計算Peter的哈希值是8784,然后到哈希索引中找到對應的行指針,根據(jù)指針找到對應的數(shù)據(jù)行。
? ? ? ?索引只存儲哈希碼及行指針,所以索引的數(shù)據(jù)結構非常的緊湊,這也讓哈希索引查找速度非???,但是哈希索引也有他的限制。

哈希索引限制

  1. 哈希索引只保存哈希碼和指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行。不過訪問內存中的行速度非常快(因為是MEMORY引擎),所以對性能影響并不大
  2. 哈希索引數(shù)據(jù)并不是按照索引值順序存儲的,所以無法用于排序
  3. 哈希索引不支持部分索引列查找,因為哈希索引始終是使用索引列的全部內容來計算哈希碼。
    如,在數(shù)據(jù)列(A,B)上建立哈希索引,如果查詢只有數(shù)據(jù)列A,則無法使用該哈希索引
  4. 哈希索引只支持等值比較查詢,包括=、IN()、<=>,不支持范圍查詢,如where price > 100
  5. 哈希沖突(不同索引列會用相同的哈希碼)會影響查詢速度,此時需遍歷索引中的行指針,逐行進行比較。
  1. 如果哈希沖突很多,一些索引維護操作的代價會很高。

? ? ? ?如果從表中刪除一行,需要遍歷鏈表中的每一行,找到并刪除對應行的引用,沖突越多,代價越大。
? ? ? ?總結:哈希索引限制多,只適用于一定的場合。而一旦適合哈希索引,它帶來的性能提升將非常顯著。

自定義哈希索引

? ? ? ?在InnoDB中,某些索引值被使用的非常頻繁的時候,它會在內存中基于B+Tree的基礎上再創(chuàng)建一個哈希索引,使其不必要在從根節(jié)點就行查找。完全自動的內部行為,用戶無法配置或更改。

使用場景

為超長的鍵創(chuàng)建哈希索引。列值太長,導致索引體積過大,查詢速度也會受到影響。

創(chuàng)建思路

? ? ? ?增加一個額外哈希列,將列值映射成哈希值,對哈希列進行再進行索引。在where條件處手動指定使用哈希函數(shù)。

假設使用的是哈希函數(shù)hash(),查詢語句如下:

select * from table where 列B=
hash('https://blog.csdn.net/qq_26222859/article/details/1')
and 列A=‘https://blog.csdn.net/qq_26222859/article/details/1'

列B還是利用B+Tree索引進行查找,只不過我們是利用哈希值而不是列鍵本身進行索引。

實例

CREATE TABLE `url_hash` (
  `url` varchar(255) DEFAULT NULL,
  `url_crc` bigint(10) DEFAULT NULL,
  KEY `HASHINDEX` (`url_crc`) USING BTREE
) ENGINE=InnoDB;

url鍵查詢

select * from url_hash where url='https://blog.csdn.net/qq_2622285'

使用mysql自帶的CRC32函數(shù)對url做哈希處理,就可以使用下面的函數(shù)查詢

select * from url_hash where url_crc=CRC32('https://blog.csdn.net/qq_2622285' ) and  url='https://blog.csdn.net/qq_2622285' 

mysql優(yōu)化器會選擇性能高且體積小的基于url_crc列的索引來完成查找,即使用多個相同的索引值,查找仍然很快。

但是,我們需要手動維護crc_url哈希列,可通過觸發(fā)器在插入和更新時實時維護url_crc列,如下

CREATE DEFINER=`root`@`localhost` TRIGGER `CRC_INS` BEFORE INSERT ON `url_hash` FOR EACH ROW begin
set NEW.url_crc=crc32(NEW.url);
end;

CREATE DEFINER=`root`@`localhost` TRIGGER `CRC_UPD` BEFORE UPDATE ON `url_hash` FOR EACH ROW begin
set NEW.url_crc=crc32(NEW.url);
end;

驗證:

insert into url_hash(url) values ('https://blog.csdn.net/qq_2622285')
select * from url_hash
update url_hash set url ='update'
select * from url_hash

select * from url_hash where url='https://blog.csdn.net/qq_2622285' and url_crc=CRC32('https://blog.csdn.net/qq_2622285') 

注意,

1、where語句中必須包含url,避免哈希沖突。

2、mysql同時提供了SHA1()、MD5()兩個加密函數(shù),不要使用這兩個函數(shù)做哈希函數(shù),他們是強加密函數(shù),設計目標是最大限度消除沖突,但計算的哈希值很長,浪費空間且有時更慢。哈希沖突只要在一個可接受的范圍內對性能影響并不大。

select SHA1('CONGZHIZHI')
select MD5('CONGZHIZHI')

空間數(shù)據(jù)索引

? ? ? ?MyISAM存儲引擎支持空間索引,可以用作地理數(shù)據(jù)存儲。和B+Tree索引不同,這類索引無需前綴查詢??臻g索引從所有維度索引數(shù)據(jù)。查詢時,可以有效地使用任意維度來組合查詢。必須使用Mysql的GIS相關函數(shù)如MBRCONTAINS()等來維護數(shù)據(jù)。Mysql 的GIS并不完善,大部分人不會使用到這個特性。開源關系數(shù)據(jù)庫中對GIS的解決方案做得比較好的是PostgreSQL的PostGIS。

全文索引

? ? ? ?全文索引是一種特殊類型的索引,它查找的是文本中的關鍵字,而不是直接比較索引中值。全文索引和其他類索引的匹配方式完全不一樣。它有許多需要注意的細節(jié),如停用詞、詞干、復數(shù)和布爾搜索等。全文索引更類似于搜索引擎做的事情,而不是簡單的where條件匹配。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容