尽管查询很简单,但下面的执行计划似乎令人失望且次优。
我正在使用MySQL 5.7。这是fiddle(尽管它仅提供5.6)。
CREATE TABLE `event` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(63) CHARSET ASCII COLLATE ASCII_BIN NOT NULL,
`is_sequenced` TINYINT(3) UNSIGNED NOT NULL,
`sequence_number` BIGINT(20) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Name-SequenceNumber` (`name`,`sequence_number`),
KEY `Name-IsSequenced` (`name`,`is_sequenced`,`id`)
) ENGINE=INNODB
;
INSERT INTO `event`
(id, `name`, is_sequenced, sequence_number)
VALUES
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL),
(NULL, 'OrderCreated', 0, NULL)
;
我们将使用Name-IsSequenced
二级索引。让我们尝试以下EXPLAIN
。 (查询位于Fiddle中。打开“查看执行计划”以查看其EXPLAIN
结果。)
EXPLAIN
SELECT * -- This part needs the PK
FROM `event` e
WHERE e.name = 'OrderCreated'
AND e.is_sequenced = 0
AND e.id <= 3
;
到目前为止,很好。 Using index condition
很有意义:整个条件都可以在期望的索引Name-IsSequenced
上解决,然后需要PK才能获得SELECT *
的剩余数据。
如果我们仅选择属于二级索引的内容,我们应该能够将其提高到Using index
,对吗? (请注意,PK始终是任何二级索引的一部分,但我们甚至可以通过在二级索引的末尾包含id
来确保这一点。结果应该是相同的。)]
EXPLAIN SELECT id FROM `event` e WHERE e.name = 'OrderCreated' AND e.is_sequenced = 0 AND e.id <= 3 ;
现在,结果为
Using where; Using index
。等等,那是……更糟?!我们减少了工作量,该计划表明它正在努力工作。
Using index
应该是可以实现的。找到name=OrderCreated
的范围,然后在其中找到is_sequenced=0
的子范围,然后在里面找到id<=3
的子范围。
[奇怪的是,我还有其他实验(有更多数据),在这些实验中,我可以通过将Using index
更改为id<=3
(与id=3
结合使用以防止它偏爱PK)来获得FORCE INDEX
。我看不出有什么区别。 (如果我们使用Fiddle尝试此操作,则它保持不变-可能是因为数据集很小。)
谁能解释为什么执行计划未表明预期有效使用二级索引?有没有一种方法可以理顺它?
尽管查询很简单,但下面的执行计划似乎令人失望且次优。我正在使用MySQL 5.7。这是小提琴(尽管它仅提供5.6)。创建表`event`(...
WHERE e.name = 'OrderCreated'
AND e.is_sequenced = 0
AND e.id <= 3