MySQL索引之哈希索引

mysql-log


0x00.About

索引(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;

這個時候,好比說,用戶登錄,我須要經過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 |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+

發現 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 ;

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

mysql> SELECT email, email_hash FROM User WHERE email_hash = CRC32("F2dgTSWRBXSZ1d3O@gmail.com") AND email = "F2dgTSWRBXSZ1d3O@gmail.com";

+----------------------------+------------+
| email                      | email_hash |
+----------------------------+------------+
| F2dgTSWRBXSZ1d3O@gmail.com | 2765311122 |
+----------------------------+------------+

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

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



0x02.Hash Index 缺點

哈希索引也有幾個缺點:

  • 索引存放的是hash值,因此僅支持 < = > 以及 IN 操做

  • hash索引沒法經過操做索引來排序,由於存放的時候通過hash計算,可是計算的hash值和存放的不必定相等,因此沒法排序

  • 不能避免全表掃描,只是因爲在memory表裏支持非惟一值hash索引,就是不一樣的索引鍵,可能存在相同的hash值

  • 若是哈希碰撞不少的話,性能也會變得不好

  • 哈希索引沒法被用來避免數據的排序操做



參考:

1] Baron Scbwartz等 著,王小東等 譯;[高性能MySQL(High Performance MySQL);電子工業出版社,2010

2] [《MySQL的B-Tree索引和Hash索引的區別》

3] [《mysql 索引優化 btree hash rtree》


本文出自 夏日小草,轉載請註明出處:http://homeway.me/2015/09/13/mysql-hash-index

-by小草

2015-09-13 16:27:10

相關文章
相關標籤/搜索