索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就能夠獲得索引的本質:索引是數據結構。mysql
從MySQL邏輯架構來看,MySQL有三層架構,第一層鏈接,第二層查詢解析、分析、優化、視圖、緩存,第三層,存儲引擎。算法
索引經過分開查詢片,節省了掃描查找時間,大大提高查詢效率。sql
大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構。數據庫
索引主要在存儲引擎層上,不一樣的引擎也就有不一樣的B-Tree算法。緩存
哈希索引只有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
FROMUser
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查詢,性能就比字符串比對查詢快多了。
創建哈希索引,先選定哈希算法,這裏選用CRC32。
《高性能MySQL》說到的方法CRC32算法,創建SHA或MD5算法是划算的,自己位數都有可能比email段長了。
在表中添加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_hash
FROMUser
WHERE email_hash = CRC32("F2dgTSWRBXSZ1d3O@gmail.com") AND
+----------------------------+------------+ | email | email_hash | +----------------------------+------------+ | F2dgTSWRBXSZ1d3O@gmail.com | 2765311122 | +----------------------------+------------+
在沒創建hash索引時候,請求時間是 0.2374 seconds,創建完索引後,請求時間直接變成 0.0003 seconds。
AND email = "F2dgTSWRBXSZ1d3O@gmail.com"
是爲了防止哈希碰撞致使數據不許確。
哈希索引也有幾個缺點:
索引存放的是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》
-by小草
2015-09-13 16:27:10