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

评论

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

我只是认为哈希索引是如此...基本...我认为存在与实现相关的特定原因。
@Alex:我敢打赌,原因是“懒惰”和“官僚主义”,但让我们等待答案))

阅读以下dev.mysql.com/doc/refman/5.1/en/innodb-adaptive-hash.html和dev.mysql.com/doc/refman/5.1/en/innodb-index-types.html

我在《高性能MySQL手册》的结尾处添加了一个不错的HASH算法。

#1 楼

许多数据库根本不支持基于哈希的索引。

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

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

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

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

评论


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

–起搏器
2012年7月6日在8:40



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

– David Spillett
2012年7月7日在11:49

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

– SOFe
19-2-28在3:19



#2 楼

有趣的是:

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

CREATE INDEX语法MySQL页面指出只有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页具有这种快速且我喜欢的-dirty算法:

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;


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

试试看! !!

评论


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

– David Spillett
2011年5月19日13:58

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

– RolandoMySQLDBA
2011年5月19日14:46



@RolandoMySQLDBA您能否详细介绍“自定义哈希”部分,最后一段似乎没有太多线索...

–起搏器
2012年7月6日在8:36

#3 楼

在相关说明中,您可能会发现PostgreSQL文档中有关索引类型的讨论很有趣。它在最新版本的文档中不再存在(由于后续的优化,我认为是这样),但是对于MySQL来说可能是相似的(以及哈希索引仅用于堆表的原因):

http://www.postgresql.org/docs/8.1/static/indexes-types.html


注意:测试表明PostgreSQL的哈希索引的性能不比B-好树索引,并且哈希索引的索引大小和构建时间要差得多。此外,哈希索引操作目前还没有进行WAL记录,因此在数据库崩溃后可能需要使用REINDEX重建哈希索引。由于这些原因,目前不鼓励使用哈希索引。
同样,与GiST索引的等效操作相比,R树索引似乎没有任何性能优势。像哈希索引一样,它们也不会进行WAL记录,并且可能在数据库崩溃后需要重新索引。发布。鼓励用户将使用R树索引的应用程序迁移到GiST索引。


同样,它(特定于PostgreSQL)(已过时),但应提示“自然”索引类型不一定会产生最佳性能。

#4 楼

对于单行查找,BTree没有比Hash慢多少。由于BTree提供了非常有效的范围查询,为什么还要烦恼除BTree之外的任何事情。是所有查询中消耗时间最多的时间。