【问题标题】:Why can MySQL not use a partial primary key index?为什么 MySQL 不能使用部分主键索引?
【发布时间】:2019-02-08 07:13:02
【问题描述】:

描述use of index extensions的MySQL文档,给出了下表作为示例,然后是下面的查询:

CREATE TABLE t1 (
    i1 INT NOT NULL DEFAULT 0,
    i2 INT NOT NULL DEFAULT 0,
    d DATE DEFAULT NULL,
    PRIMARY KEY (i1, i2),
    INDEX k_d (d)
) ENGINE = InnoDB;

SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';

InnoDB 内部会将索引k_d 转换为最后包含主键。也就是说,实际的索引k_d 将在(d, i1, i2) 上,三列。

文档继续解释(强调我的):

优化器不能在这种情况下使用主键,因为它包含列 (i1, i2) 并且查询不引用 i2。相反,优化器可以使用 (d) 上的二级索引 k_d,执行计划取决于是否使用扩展索引。

我对上面的说法感到困惑。首先它说i1 不足以 使用两列(i1, i2) 的主键索引。然后,在第二句中,它说(d, i1, i2) 上的索引k_d 可以 被使用,尽管只有di1 被使用,i2 不存在。

我对 MySQL 和其他 SQL 风格中的索引的一般理解是,如果索引中所有列的子集从左侧开始,则可以使用索引的左侧部分。

主键(聚集)索引和非聚集二级索引允许后者使用部分索引,而前者不能?

【问题讨论】:

标签: mysql indexing clustered-index


【解决方案1】:

您链接到的页面上的文档部分不准确。

演示,在 MySQL 5.7.21 上运行:

mysql [localhost] {msandbox} (test) > CREATE TABLE t1 (
    ->     i1 INT NOT NULL DEFAULT 0,
    ->     i2 INT NOT NULL DEFAULT 0,
    ->     d DATE DEFAULT NULL,
    ->     PRIMARY KEY (i1, i2),
    ->     INDEX k_d (d)
    -> ) ENGINE = InnoDB;

mysql [localhost] {msandbox} (test) > explain SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ref  | PRIMARY,k_d   | PRIMARY | 4       | const |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+

此查询选择 PRIMARY 索引,您可以看到 key_len 为 4,证明它将仅使用 32 位 INT 列之一。

在使用 InnoDB 表时,MySQL 通常更喜欢使用 PRIMARY 索引(聚集索引),因为它比使用二级索引更有效。

【讨论】:

  • 这听起来像是很好的侦探工作。如果你不介意的话,我想等一下再给你战利品。
  • 同意。多列索引可以使用最左边的前缀,奇怪的是,在同一段落中它说它不能在主索引中使用i1...老实说,整个页面可以通过删除关于的措辞来清理作为页面主题的主索引仅涵盖扩展二级索引。
  • Documentation here 关于多列索引如何使用最左前缀,这与 OP 所在的段落相矛盾。
  • 我让技术作家松了一口气。 MySQL 手册长达 4000 多页,其中一些主题甚至软件开发人员都无法理解。
  • 比尔是对的。这里可以使用主索引和二级索引的前缀。选择哪个索引将是查询优化器基于成本的决定。我已经提交了一个文档错误:bugs.mysql.com/92347
【解决方案2】:

在这种情况下

WHERE i1 = 3 AND d = '2000-01-01';

我更喜欢明确地INDEX(d, i1)(或INDEX(i1, d))。我的理由是我告诉读者我考虑过索引并意识到这最适合查询。这将是一个“覆盖”索引,因此会更快一些。

当然,您拥有的INDEX(d)应该等同于INDEX(d, i1, i2),应该有效且高效地使用它。至于为什么,我猜是优化器的疏忽。

至于文档,有一些措辞不佳的地方。他们欢迎 bugs.mysql.com 上的文档评论。

【讨论】:

  • 查看Bill Karwin 的回答,这表明实际上优化器确实 使用了覆盖索引;没有监督AFAIK。同意你对 MySQL 文档的看法。它具有开源的味道。
  • @TimBiegeleisen - “使用索引” == 'covering',“使用 where” 意味着别的东西。看到 Oystein 写了一个文档错误报告(上面的评论)。
猜你喜欢
  • 2018-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
相关资源
最近更新 更多