我有一个仅在相等时选择的应用程序,我认为我应该在btree索引上使用哈希索引。令我沮丧的是,MyISAM或InnoDB不支持哈希索引。那是怎么回事?
我有一个仅在相等时选择的应用程序,我认为我应该在btree索引上使用哈希索引。令我沮丧的是,MyISAM或InnoDB不支持哈希索引。那是怎么回事?
Answers:
许多数据库不支持基于散列索引可言。
为了使哈希表高效,您需要知道可能存在的行数,否则基本哈希表将太大(许多空条目,浪费空间和潜在的磁盘IO)或太小意味着经常使用间接(可能有多个间接级别,或者如果哈希实现是单层,甚至可能更糟,则最终可能会在相当数量的记录上执行线性搜索),此时,事情的效率可能不会比基于树的效率更高反正索引。
因此,为了通常有用(即通常比替代方法更好),索引需要随数据增长(和收缩)而偶尔重建,这可能会增加大量的间歇性开销。这对于基于内存的表通常很好,因为重建可能会非常快(因为数据始终将存储在RAM中,在任何情况下都不会很庞大),但是在磁盘上重建大索引是一种困难。非常繁重的操作(并且IIRC mySQL不支持实时索引重建,因此在操作期间持有表锁)。
因此,在内存表中使用哈希索引,因为它们通常具有更好的性能,但是基于磁盘的表不支持它们,因为它们可能会对性能造成不利影响,而不是额外的好处。当然,没有什么可以阻止哈希索引可用于基于磁盘的表,毫无疑问,某些数据库确实支持该功能,但是大概是在ISAM / InnoDB表中没有实现,因为维护者认为该功能不值得添加(因为在少数情况下,产生并产生重大影响的额外代码编写和维护并不值得。如果您强烈不同意,可以与他们联系,并为实现该功能提供充分的理由。
如果您正在索引大型字符串,则可以实现自己的伪哈希索引(通过存储值和实际值的哈希,并具有列的索引),但这绝对对大型字符串(其中计算散列值并以此值搜索树索引总是比使用较大的值搜索树索引进行比较总是更快,并且使用的额外存储空间不会很大),因此在实施之前进行一些性能分析这在生产中。
在相关说明中,您可能会发现PostgreSQL文档中有关索引类型的讨论很有趣。它在最新版本的文档中不再存在(由于后续的优化,我认为是这样),但是对于MySQL来说可能是相似的(以及哈希索引仅用于堆表的原因):
http://www.postgresql.org/docs/8.1/static/indexes-types.html
注意:测试表明PostgreSQL的哈希索引的性能不比B树索引好,并且哈希索引的索引大小和构建时间要差得多。此外,哈希索引操作目前还没有进行WAL记录,因此在数据库崩溃后可能需要使用REINDEX重建哈希索引。由于这些原因,目前不鼓励使用哈希索引。同样,与GiST索引的等效操作相比,R树索引似乎没有任何性能优势。像哈希索引一样,它们也不WAL记录,并且在数据库崩溃后可能需要重新索引。尽管哈希索引的问题最终可能会得到解决,但R-tree索引类型很可能在将来的版本中淘汰。鼓励用户将使用R树索引的应用程序迁移到GiST索引。
同样,它(特定于PostgreSQL)(过时的版本),但是它应该暗示“自然”索引类型不一定会产生最佳性能。
这是一些有趣的事情:
根据《MySQL 5.0认证研究指南》第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没有抱怨。
更新
坏消息 !!!我使用了SHOW INDEXES FROM。它说索引是BTREE。
MySQL页面的CREATE INDEX语法指出,只有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;
在任何表中为此添加一个列,并为此值编制索引。
试试看 !!!