為什麼 MySQL 在 MyISAM 或 InnoDB 上沒有雜湊索引?
我有一個只會選擇相等的應用程序,我想我應該在 btree 索引上使用雜湊索引。令我沮喪的是,MyISAM 或 InnoDB 不支持雜湊索引。那是怎麼回事?
許多數據庫根本不支持基於雜湊的索引。
為了使雜湊表高效,您需要知道可能存在的行數,否則基本雜湊表將太大(許多空條目、浪費空間和潛在的磁碟 IO)或太小意味著經常使用間接(可能是多級間接,或者更糟糕的是,如果散列實現是單級的,您最終可能會對相當數量的記錄執行線性搜尋),此時事情可能不會比基於樹的效率更高反正索引。
因此,為了普遍有用(即通常比替代方案更好),索引需要隨著數據的增長(和縮小)而偶爾重建,這可能會增加顯著的間歇性成本。這通常適用於基於記憶體的表,因為重建可能會非常快(因為數據總是在 RAM 中並且在任何情況下都不可能很大),但是在磁碟上重建大索引是一個非常繁重的操作(並且 IIRC mySQL 不支持實時索引重建,因此在操作期間持有表鎖)。
因此,雜湊索引用於記憶體表,因為它們通常性能更好,但基於磁碟的表不支持它們,因為它們可能會損害性能而不是獎勵。當然,沒有什麼可以阻止雜湊索引可用於基於磁碟的表,毫無疑問,某些數據庫確實支持該功能,但可能它們沒有在 ISAM/InnoDB 表中實現,因為維護者不認為值得添加的功能(因為編寫和維護的額外程式碼在它產生顯著差異的少數情況下是不值得的)。也許如果您強烈反對,您可以與他們交談並為該功能的實現提供一個很好的案例。
如果您正在索引大字元串,那麼實現您自己的偽雜湊索引(通過儲存值的雜湊以及實際值,以及具有列的索引)可能會起作用,但這對於大字元串肯定更有效(其中計算雜湊值並通過該值搜尋樹索引總是可能比僅使用較大的值搜尋樹索引進行比較更快,並且使用的額外儲存不會很重要)所以在實現之前做一些性能分析這在生產中。
這裡有一些有趣的東西:
根據書MySQL 5.0 Certification Study Guide,第 433 頁,第 29.5.1 節
MEMORY 引擎預設使用 HASH 索引算法。
為了笑,我嘗試在 MySQL 5.5.12 中使用 HASH 創建一個 InnoDB 表和一個帶有主鍵的 MyISAM 表
mysql> use test Database changed mysql> create table rolando (num int not null, primary key (num) using hash); Query OK, 0 rows affected (0.11 sec) mysql> show create table rolando\G *************************** 1. row *************************** Table: rolando Create Table: CREATE TABLE `rolando` ( `num` int(11) NOT NULL, PRIMARY KEY (`num`) USING HASH ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec) mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM; Query OK, 0 rows affected (0.05 sec) mysql> show create table rolando2\G *************************** 1. row *************************** Table: rolando2 Create Table: CREATE TABLE `rolando2` ( `num` int(11) NOT NULL, PRIMARY KEY (`num`) USING HASH ) ENGINE=MyISAM DEFAULT CHARSET=latin1 1 row in set (0.00 sec)
MySQL 沒有抱怨。
更新
壞消息 !!!我使用了顯示索引。它說索引是BTREE。
CREATE INDEX 語法 MySQL Page聲明只有 MEMORY 和 NDB 儲存引擎可以容納 HASH INDEX。
mysql> show indexes from rolando; +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | rolando | 0 | PRIMARY | 1 | num | A | 0 | NULL | NULL | | BTREE | | | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec) mysql> show indexes from rolando2; +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | rolando2 | 0 | PRIMARY | 1 | num | A | 0 | NULL | NULL | | BTREE | | | +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec) mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY; Query OK, 0 rows affected (0.03 sec) mysql> show create table rolando3\G *************************** 1. row *************************** Table: rolando3 Create Table: CREATE TABLE `rolando3` ( `num` int(11) NOT NULL, PRIMARY KEY (`num`) ) ENGINE=MEMORY DEFAULT CHARSET=latin1 1 row in set (0.00 sec) mysql> show indexes from rolando3; +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | rolando3 | 0 | PRIMARY | 1 | num | NULL | 0 | NULL | NULL | | HASH | | | +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec)
有人建議按照“高性能 MySQL:優化、備份、複製等”一書第 102-105 頁中的想法來模擬雜湊算法。
第 105 頁具有我喜歡的這種快速而骯髒的算法:
SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;
在任何表中為此創建一列並索引該值。
試一試 !!!