MySQL中的雜湊索引
Memory中的雜湊索引
雜湊索引是基於雜湊表實現的,只有精確匹配索引所有列的查詢才有效。對於每一行資料,儲存引擎都會對所有的索引列計算一個雜湊碼,雜湊碼是一個較小的值,並且不同鍵值的行計算出來的雜湊碼也不一樣。雜湊索引將所有的雜湊碼存貯在索引中,同時在雜湊表中儲存指向每個資料行的指標。
在MySQL中只有Memory引擎顯式支援雜湊索引。這也是Memory引擎表的預設索引型別。
Memory引擎支援非唯一索引,這在資料庫世界裡是比較與眾不同的。如果多個列的雜湊值相同,索引會以連結串列的方式存放多個記錄指標到同一個雜湊列表中。
建立一個使用hash索引的表,
CREATE TABLE testhash ( fname VARCHAR(50) NOT NULL, lname VARCHAR(50) NOT NULL, KEY USING HASH(fname) ) ENGINE=MEMORY;
用下式查詢,
mysql> SELECT lname FROM testhash WHERE fname='Peter';
mysql先計算“Peter”的雜湊值,並使用該值尋找對應的記錄指標。找到指標指向的行後,比較fname是否為“Peter”,以確保是查詢的行。
因為索引自身只需存貯對應的雜湊值,所以索引的結構十分緊湊,這也讓雜湊索引查詢的速度非常快。
然而也有侷限,懶得敲字了,直接上圖
因為這些限制,雜湊索引只適用於某些特定的場合。然而一旦使用雜湊索引,則它帶來的效能提升非常顯著
InnoDB中的自適應雜湊索引
InnoDB引擎中有一個特殊的功能叫做“自適應雜湊索引(adaptive hash index)”。當InnoDB注意到某些索引值被使用的非常頻繁時,它會在記憶體中基於B-Tree
可以通過引數 innodb_adaptive_hash_index 檢視是否開啟。預設是開啟的。
mysql> show variables like "innodb_adaptive_hash_index"; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_adaptive_hash_index | ON | +----------------------------+-------+
關閉自適應雜湊索引,
mysql> set innodb_adaptive_hash_index = 0;
建立自定義雜湊索引
如果儲存引擎不支援雜湊索引,則可以類似於InnoDB建立雜湊索引,這樣可以享受一些雜湊索引的便利。
思路: 在B-Tree基礎上建立一個偽雜湊索引。這和真正的雜湊索引不是一回事,因為還是使用B-Tree進行查詢,但是使用雜湊值而不是鍵本身進行索引查詢,你需要在查詢的where子句中手動指定使用雜湊函式。
例項: 如需要儲存大量URL,並並需要根據URL進行搜尋查詢。如果使用B-Tree來儲存URL,存貯的內容會很大。
一般情況下的查詢語句,
mysql> SELECT id FROM url WHERE url = 'https://my.oschina.net/depeng414';
若刪除原來url列上的索引,而新增一個被索引的url_crc列,使用CRC32做雜湊,就可以這樣查詢,效能更高。
mysql> SELECT id FROM url WHERE url = 'https://my.oschina.net/depeng414'
-> AND url_crc = CRC32('https://my.oschina.net/depeng414');
因為MySQL優化器會使用這個選擇性很高而體積很小的基於url_crc列的索引來完成查詢(只需根據雜湊值做快速的整數比較就能找到索引條目)。
當然,沒有免費的午餐,為了查詢的速度就要犧牲維護的便捷性。這樣實現的缺陷就是需要維護雜湊值。
可以手動維護或者選擇觸發器實現。先建立表,
CREATE TABLE pseudohash (
id int unsigned NOT NULL auto_increment,
url varchar(225) NOT NULL,
url_crc int unsigned NOT NULL DEFAULT 0,
PRIMARY KEY(id)
);
然後建立觸發器。先臨時修改下語句分隔符,這樣就可以在觸發器定義語句中使用分號
mysql> delimiter //
mysql> create trigger pseudohash_crc_ins before insert on pseudohash for each row begin
-> set new.url_crc=crc32(new.url);
-> end;
-> //
Query OK, 0 rows affected
mysql> delimiter //
mysql> create trigger pseudohash_crc_upd before update on pseudohash for each row begin
-> set new.url_crc=crc32(new.url);
-> end;
-> //
Query OK, 0 rows affected
delimiter ;
驗證下,
mysql> insert into pseudohash (url) values ('http://www.baidu.com');
Query OK, 1 row affected
mysql> select * from pseudohash;
+----+----------------------+------------+
| id | url | url_crc |
+----+----------------------+------------+
| 1 | http://www.baidu.com | 3500265894 |
+----+----------------------+------------+
1 row in set
切記,不要使用 SHA1() 和 MD5() 作為雜湊函式,這兩個函式是強加密函式,設計目標是最大限度消除雜湊衝突,這裡不需要這樣的高的要求。
如果資料表非常大,可以自己實現一個雜湊函式,這裡就不敲了,我想絕大多數java程式設計師大多時候也可不會用到。即使用到時再