1. 程式人生 > >MySQL中的雜湊索引

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

索引上再建立一個雜湊索引,這樣就讓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程式設計師大多時候也可不會用到。即使用到時再