为什么MySQL不能使用部分主键索引?
Why can MySQL not use a partial primary key index?
描述 use of index extensions 的 MySQL 文档给出了以下 table 作为示例,后跟以下查询:
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)
三列上。
文档继续解释(强调我的):
The optimizer cannot use the primary key in this case because that comprises columns (i1, i2) and the query does not refer to i2. Instead, the optimizer can use the secondary index k_d on (d), and the execution plan depends on whether the extended index is used.
我被上面的说法搞糊涂了。首先它说i1
是不足以使用两列(i1, i2)
的主键索引。然后,在第二句中,它说索引 k_d
on (d, i1, i2)
can be used, despite that only only d
and i1
正在使用,i2
不存在。
我对 MySQL 和其他 SQL 中的索引的一般理解是,如果存在索引中所有列的子集,则可以使用索引的左侧部分, 从左边开始。
主键(聚簇)索引和非聚簇二级索引允许后者使用部分索引,而前者不能?有什么不同?
您链接到的页面上的文档部分不准确。
演示,运行 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 |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
此查询选择主索引,您可以看到 key_len
是 4,证明它将仅使用 32 位 INT 列之一。
使用 InnoDB 表时,MySQL 通常更喜欢使用 PRIMARY 索引(聚集索引),因为它比使用二级索引更有效。
在这种情况下
WHERE i1 = 3 AND d = '2000-01-01';
我更喜欢明确地说INDEX(d, i1)
(或INDEX(i1, d)
)。我的理由是我告诉 reader 我考虑了索引并意识到这最适合查询。这将是一个 "covering" 索引,因此速度更快。
当然,您拥有的 INDEX(d)
应该 等同于 INDEX(d, i1, i2)
,应该有效且高效地使用。至于为什么,我猜是优化器的疏忽。
至于文档,有一些措辞不当的地方。他们欢迎 bugs.mysql.com 上的文档批评。
描述 use of index extensions 的 MySQL 文档给出了以下 table 作为示例,后跟以下查询:
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)
三列上。
文档继续解释(强调我的):
The optimizer cannot use the primary key in this case because that comprises columns (i1, i2) and the query does not refer to i2. Instead, the optimizer can use the secondary index k_d on (d), and the execution plan depends on whether the extended index is used.
我被上面的说法搞糊涂了。首先它说i1
是不足以使用两列(i1, i2)
的主键索引。然后,在第二句中,它说索引 k_d
on (d, i1, i2)
can be used, despite that only only d
and i1
正在使用,i2
不存在。
我对 MySQL 和其他 SQL 中的索引的一般理解是,如果存在索引中所有列的子集,则可以使用索引的左侧部分, 从左边开始。
主键(聚簇)索引和非聚簇二级索引允许后者使用部分索引,而前者不能?有什么不同?
您链接到的页面上的文档部分不准确。
演示,运行 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 |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
此查询选择主索引,您可以看到 key_len
是 4,证明它将仅使用 32 位 INT 列之一。
使用 InnoDB 表时,MySQL 通常更喜欢使用 PRIMARY 索引(聚集索引),因为它比使用二级索引更有效。
在这种情况下
WHERE i1 = 3 AND d = '2000-01-01';
我更喜欢明确地说INDEX(d, i1)
(或INDEX(i1, d)
)。我的理由是我告诉 reader 我考虑了索引并意识到这最适合查询。这将是一个 "covering" 索引,因此速度更快。
当然,您拥有的 INDEX(d)
应该 等同于 INDEX(d, i1, i2)
,应该有效且高效地使用。至于为什么,我猜是优化器的疏忽。
至于文档,有一些措辞不当的地方。他们欢迎 bugs.mysql.com 上的文档批评。