作为枚举索引的类型,BTREE 或 HASH 是否更小?

Is BTREE or HASH smaller as a type for enum indexing?

我的任务是优化 table 的磁盘大小。假设我们有一个像这样的 table:

users(id, <some other fields>, role)

其中 role 是某个较大的最大尺寸的 varchar,但是如果我 运行

select distinct `role`
from users;

我得到三个值:

admin

regular

guest

涉及数百万条记录,很明显,由于它是 varchar,每条记录分配 character_size * 长度字节。

我提出了将 role 更改为 enum 的想法,因为它会将数值映射到引擎盖下的每个可能值,并且实际文本只存储一次,在 table 水平。到目前为止,还不错。

现在,role 字段有一个 BTREE 类型的 index,我计划在列更改后使用此脚本:

DROP INDEX `Role` ON users;
CREATE INDEX `Role` ON users(`role`) USING BTREE;

我的问题是:另一种类型,比如 HASH 在这种情况下会比 BTREE 节省更多 space 吗?或者,更广泛地说:就大小而言,是否有比 BTREE 更好的索引类型?

索引的大小取决于您使用的 table 引擎。对于 MySQL 你有 InnoDB 和 MyISAM。 MyISAM 会将索引存储在单独的文件中,而 InnoDB 会将索引与 table 数据一起存储(并且不提供 HASH 索引)。我认为您不会想要使用 MyISAM,因为它缺乏 InnoDB 具有的完整性功能。

我认为 HASH 索引在这里不是一个好主意,因为您最终只会得到三个哈希值,因为 role 列只有三种可能性。索引的基数将很低。这样,您的选项就变成了 BTREE。

散列索引将是每行一个数字(它的散列号)和一个指向一行的指针,所以我会说两个整数。 InnoDB 二级索引将是一个值加上一个指向 table 的聚集索引的指针,因此它们的大小相同。如果索引是聚簇索引,也会包含table的主键,而且会更大

InnoDB 不支持 HASH 索引。在 MySQL 8.0 中,他们终于创建了一个警告,因此当您请求不支持的 HASH 索引时,您可以知道它没有按照您的要求执行:

mysql> create table users (id serial primary key, role varchar(10));
Query OK, 0 rows affected (0.03 sec)

mysql> create index role on users(role) using hash;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 1

mysql> show warnings;
+-------+------+---------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                 |
+-------+------+---------------------------------------------------------------------------------------------------------+
| Note  | 3502 | This storage engine does not support the HASH index algorithm, storage engine default was used instead. |
+-------+------+---------------------------------------------------------------------------------------------------------+

在MySQL之前的版本中,它会默默接受你的createtable语句,SHOW CREATE TABLE甚至会显示该索引是一个HASH索引,但这是一个谎言。它被创建为 BTREE 索引。

https://dev.mysql.com/doc/refman/8.0/en/create-index.html 中说:

保留 HASH 索引定义的混乱行为的理由非常薄弱:如果您使用 USING HASH 定义索引,然后更改 table 以使用 MEMORY 存储引擎,则索引选项将被支持。这是在这个错误的评论中:https://bugs.mysql.com/bug.php?id=22632

在我看来,这并不能很好地解释这种行为造成的混乱程度。


要处理不断增长的数据库,最好使用其他传统技术:

两种索引类型对

都没有用
INDEX(role)

除非您要测试的 role 很少见。这是因为优化器将避开 INDEX 如果它不是很有选择性。因此,通过摆脱那个无用的索引来节省 space。

另一方面,

INDEX(role, some-other-column)

可能非常有用,无论 role 的基数如何。拥有索引可能是值得的,即使它需要 space。值得拥有吗?答案取决于可能使用此类索引的查询。

至于 BTree 与哈希——请注意 MySQL 没有费心去实现哈希。毕竟,Btree 和 Hash 差不多快,plus 对范围很有用,不像 Hash。

就使用 BTree 时的 INT vs VARCHAR vs ENUM 而言——它们的工作原理几乎相同。

至于space,ENUM占1字节,为胜

至于“规范化”,嗯,一个“id”不能小于1个字节。并且查找该值需要额外的 table,等等。因此,它不是很有用 for role.

甚至不要考虑 MyISAM。它正在消失。并且在集群环境中是不允许的。

考虑索引大小的唯一原因是衡量摆脱无用索引的好处。