【问题标题】:Is BTREE or HASH smaller as a type for enum indexing?BTREE 或 HASH 作为枚举索引的类型是否更小?
【发布时间】:2021-08-17 01:38:05
【问题描述】:

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

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

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

select distinct `role`
from users;

我得到三个值:

管理员

常规

客人

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

我提出了将role 更改为enum 的想法,因为它将数值映射到引擎盖下的每个可能值,并且实际文本在表级别上仅存储一次。到目前为止,一切顺利。

现在,role 字段的 index 类型为 BTREE,我在列更改后计划了这个脚本:

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

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

【问题讨论】:

  • users 表使用什么存储引擎?如果您使用 InnoDB(这是默认设置),您是否知道 InnoDB 不支持哈希索引?即使您使用“USING HASH”语法,它也会忽略您并将索引创建为 BTREE 索引。
  • @BillKarwin innodb。答案是否定的,我没有意识到这一点。
  • 与您的问题没有直接关系,但enum 有一些您应该注意的drawbacks。根据您的用例,可能首选明确的 user_roles 表。

标签: mysql optimization size


【解决方案1】:

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 版本中,它会默默地接受您的 create table 语句,SHOW CREATE TABLE 甚至会显示该索引是 HASH 索引,但这是一个谎言。它被创建为 BTREE 索引。

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

保留 HASH 索引定义的令人困惑的行为有一个非常薄的理由:如果您使用 USING HASH 定义索引并随后更改表以使用 MEMORY 存储引擎,则将支持索引选项。这是在这个 bug 的 cmets 中:https://bugs.mysql.com/bug.php?id=22632

在我看来,这并不能很好地证明这种行为引起的混乱。


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

【讨论】:

  • 谢谢你的回答,比尔。我熟悉您所指出的技术,但不确定我所询问的这个具体项目。你的回答澄清了这个问题。谢谢!
  • "在以前的 MySQL 版本中,它会默默地接受你的 create table 语句,并且 SHOW CREATE TABLE 甚至会显示索引是 HASH 索引,但这是一个谎言。它是作为 BTREE 创建的指数。”我同意你的观点,这是误导。
【解决方案2】:

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

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

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

【讨论】:

  • 你能解释一下幕后发生了什么吗?
  • 我认为 HASH 索引仅限于 MEMORY 存储引擎。
  • Alexander,感谢您有见地的回答,我赞成,但必须接受另一个答案,因为它更详细。
  • 不客气。此外,@Bill Karwin 解释说 MySQL 一直在对我们撒谎是值得的。
【解决方案3】:

这两种索引类型都无用

INDEX(role)

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

另一方面,

INDEX(role, some-other-column)

无论role 的基数如何,它都非常有用。即使占用空间,也可能值得拥有索引。值得拥有吗?答案取决于可能使用此类索引的查询。

至于 BTree 与 Hash——注意 MySQL 并没有费心去实现 Hash。毕竟,Btree 的速度和 Hash 差不多,而且对范围很有用,不像 Hash。

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

至于空格,ENUM 占 1 个字节,胜出。

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

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

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

【讨论】:

  • 我们需要索引,因为过滤器,所以这是一个如何使用更少空间的问题。关于你的最后一句话,我相信如果 A 上有一个 1D 索引,B 上有另一个索引,并且它们被过滤在一起,那么删除它们并创建一个复合索引是有意义的。这是与问题中描述的情况不同的情况,但值得一提。
  • @LajosArpad - 这里涵盖了许多场景:mysql.rjweb.org/doc.php/index_cookbook_mysql
猜你喜欢
  • 1970-01-01
  • 2011-03-18
  • 2016-06-26
  • 2011-06-02
  • 1970-01-01
  • 2013-01-11
  • 2020-12-16
  • 2011-12-24
  • 2019-01-24
相关资源
最近更新 更多