哈希索引

索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就能夠獲得索引的本質:索引是數據結構。mysql

從MySQL邏輯架構來看,MySQL有三層架構,第一層鏈接,第二層查詢解析、分析、優化、視圖、緩存,第三層,存儲引擎。算法

MySQL邏輯架構

索引經過分開查詢片,節省了掃描查找時間,大大提高查詢效率。sql

大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構。數據庫

索引主要在存儲引擎層上,不一樣的引擎也就有不一樣的B-Tree算法。緩存

 


 

0x01.Hash Index

哈希索引只有Memory, NDB兩種引擎支持,Memory引擎默認支持哈希索引,若是多個hash值相同,出現哈希碰撞,那麼索引以鏈表方式存儲。數據結構

可是,Memory引擎表只對可以適合機器的內存切實有限的數據集。架構

要使InnoDB或MyISAM支持哈希索引,能夠經過僞哈希索引來實現,叫自適應哈希索引。性能

主要經過增長一個字段,存儲hash值,將hash值創建索引,在插入和更新的時候,創建觸發器,自動添加計算後的hash到表裏。優化

直接索引

假若有一個很是很是大的表,以下:spa

CREATE TABLE IF NOT EXISTS `User` ( `id` int(10) NOT NULL COMMENT '自增id', `name` varchar(128) NOT NULL DEFAULT '' COMMENT '用戶名', `email` varchar(128) NOT NULL DEFAULT '' COMMENT '用戶郵箱', `pass` varchar(64) NOT NULL DEFAULT '' COMMENT '用戶密碼', `last` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最後登陸時間', ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這個時候,好比說,用戶登錄,我須要經過email檢索出用戶,經過explain獲得以下:

mysql> explain SELECT id FROM User WHERE email = ‘ooxx@gmail.com’ LIMIT 1;

+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | User | ALL | NULL | NULL | NULL | NULL | 384742 | Using where | +----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
  • 1
  • 2
  • 3
  • 4
  • 5

發現 rows = 384742 也就是要在384742裏面進行比對email這個字段的字符串。

這條記錄運行的時間是:Query took 0.1744 seconds,數據庫的大小是40萬。

從上面能夠說明,若是直接在email上面創建索引,除了索引區間匹配,還要進行字符串匹配比對,email短還好,若是長的話這個查詢代價就比較大。

若是這個時候,在email上創建哈希索引,查詢以int查詢,性能就比字符串比對查詢快多了。

Hash 算法

創建哈希索引,先選定哈希算法,這裏選用CRC32。

《高性能MySQL》說到的方法CRC32算法,創建SHA或MD5算法是划算的,自己位數都有可能比email段長了。

INSERT UPDATE SELECT 操做

在表中添加hash值的字段:

mysql> ALTER TABLE User ADD COLUMN email_hash int unsigned NOT NULL DEFAULT 0;

接下來就是在UPDATE和INSERT的時候,自動更新 email_hash 字段,經過MySQL觸發器實現:

DELIMITER |
CREATE TRIGGER user_hash_insert BEFORE INSERT ON `User` FOR EACH ROW BEGIN SET NEW.email_hash=crc32(NEW.email); END; | CREATE TRIGGER user_hash_update BEFORE UPDATE ON `User` FOR EACH ROW BEGIN SET NEW.email_hash=crc32(NEW.email); END; | DELIMITER ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

這樣的話,咱們的SELECT請求就會變成這樣:

mysql> SELECT emailemail_hash FROM User WHERE email_hash = CRC32(「F2dgTSWRBXSZ1d3O@gmail.com」) AND email = 「F2dgTSWRBXSZ1d3O@gmail.com」;

+----------------------------+------------+ | email | email_hash | +----------------------------+------------+ | F2dgTSWRBXSZ1d3O@gmail.com | 2765311122 | +----------------------------+------------+
  • 1
  • 2
  • 3
  • 4
  • 5

在沒創建hash索引時候,請求時間是 0.2374 seconds,創建完索引後,請求時間直接變成 0.0003 seconds。

AND email = "F2dgTSWRBXSZ1d3O@gmail.com" 是爲了防止哈希碰撞致使數據不許確。

 


 

0x02.Hash Index 缺點

哈希索引也有幾個缺點:

  • 索引存放的是hash值,因此僅支持 < = > 以及 IN 操做
  • hash索引沒法經過操做索引來排序,由於存放的時候通過hash計算,可是計算的hash值和存放的不必定相等,因此沒法排序
  • 不能避免全表掃描,只是因爲在memory表裏支持非惟一值hash索引,就是不一樣的索引鍵,可能存在相同的hash值
  • 若是哈希碰撞不少的話,性能也會變得不好
  • 哈希索引沒法被用來避免數據的排序操做
相關文章
相關標籤/搜索