为什么MySQL在MyISAM或InnoDB上没有哈希索引?


35

我有一个仅在相等时选择的应用程序,我认为我应该在btree索引上使用哈希索引。令我沮丧的是,MyISAM或InnoDB不支持哈希索引。那是怎么回事?


2
Mysql还不支持基于函数的索引,位图索引等。仅因为它是mysql ;-)

1
我只是认为哈希索引是如此...基本...我认为存在与实现相关的特定原因。

1
@Alex:我敢打赌,原因是“懒惰”和“官僚主义”,但让我们等待答案))


我在《高性能MySQL手册》的末尾添加了一个不错的HASH算法。
RolandoMySQLDBA 2011年

Answers:


16

许多数据库不支持基于散列索引可言

为了使哈希表高效,您需要知道可能存在的行数,否则基本哈希表将太大(许多空条目,浪费空间和潜在的磁盘IO)或太小意味着经常使用间接(可能有多个间接级别,或者如果哈希实现是单层,甚至可能更糟,则最终可能会在相当数量的记录上执行线性搜索),此时,事情的效率可能不会比基于树的效率更高反正索引。

因此,为了通常有用(即通常比替代方法更好),索引需要随数据增长(和收缩)而偶尔重建,这可能会增加大量的间歇性开销。这对于基于内存的表通常很好,因为重建可能会非常快(因为数据始终将存储在RAM中,在任何情况下都不会很庞大),但是在磁盘上重建大索引是一种困难。非常繁重的操作(并且IIRC mySQL不支持实时索引重建,因此在操作期间持有表锁)。

因此,在内存表中使用哈希索引,因为它们通常具有更好的性能,但是基于磁盘的表不支持它们,因为它们可能会对性能造成不利影响,而不是额外的好处。当然,没有什么可以阻止哈希索引可用于基于磁盘的表,毫无疑问,某些数据库确实支持该功能,但是大概是在ISAM / InnoDB表中没有实现,因为维护者认为该功能不值得添加(因为在少数情况下,产生并产生重大影响的额外代码编写和维护并不值得。如果您强烈不同意,可以与他们联系,并为实现该功能提供充分的理由。

如果您正在索引大型字符串,则可以实现自己的伪哈希索引(通过存储值和实际值的哈希,并具有列的索引),但这绝对对大型字符串(其中计算散列值并以此值搜索树索引总是比使用较大的值搜索树索引进行比较总是更快,并且使用的额外存储空间不会很大),因此在实施之前进行一些性能分析这在生产中。


有什么方法可以并排完成重新哈希(重建)操作而不锁定整个表吗?
Pacerier

@Pacerier:我对MySQL不了解(尽管自从我上次使用它以来,他们可能已经添加了该功能,所以请查阅文档)。即使DBMS支持在线索引创建/重建,它也不是默认选项。锁定的内容将有所不同:有些将在表上保持写锁定,以使其他事务仅在读取时不会延迟,有些DMBS将获取完整的表锁定。如果需要在线重建,请在选择使用哪个DBMS之前检查每个DBMS的文档。
David Spillett

通常只有在数据长度加倍时才需要重建。他们真的需要担心数据长度每分钟增加一倍吗?(通常,当数据库足够大而引起关注时,这种情况很少发生)
SOFe

6

在相关说明中,您可能会发现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)(过时的版本),但是它应该暗示“自然”索引类型不一定会产生最佳性能。


5

这是一些有趣的事情:

根据《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;

在任何表中为此添加一个列,并为此值编制索引。

试试看 !!!


5
在生产中使用伪哈希索引技术之前,请对其进行一些性能分析。对于较大的字符串,它可能会产生很大的不同,但是无论如何最终还是要在树索引中进行导航,并且还需要执行额外的比较来从与哈希匹配的那些中找到正确的行,因此对于较小的值,计算哈希值和存放它们是不值得的。这根本不是一个哈希索引,您只是减少了遍历树的工作(因为每个比较都考虑较少的字节,例如,比较8字节的INT而不是x00字节的字符串)。
David Spillett

@David Spillett在此,我完全同意您的看法。同一本书的第11章“高性能索引策略”中也建议了其他索引策略。为了进一步提高我的答案,本书实际上提到使用聚集索引,该索引将行和BTree索引存储在同一结构中。这可能会加快您提到的工作量。不幸的是,您刚才提到的必须克服的障碍是不可避免的。先生,我还是对您的评论+1!实际上,您的答案也为+1。
RolandoMySQLDBA 2011年

@RolandoMySQLDBA您能否详细说明“自定义哈希”部分,最后一段似乎没有太多线索……
Pacerier 2012年

2

对于单行查找,BTree没有比Hash慢多少。因为BTree提供了非常有效的范围查询,所以为什么要麻烦除BTree之外的任何事情。

MySQL在缓存BTree块方面做得非常好,因此基于BTree的查询很少需要执行I / O,这是所有查询中最大的时间消耗。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.