我目前正在尝试优化 Doctrine 2 在此表上生成的查询:
CREATE TABLE `publication` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`global_order` int(11) NOT NULL,
`title` varchar(63) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(63) COLLATE utf8_unicode_ci NOT NULL,
`type` varchar(7) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_AF3C6779B12CE9DB` (`global_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
查询是
SELECT *
FROM publication
WHERE type IN ('article', 'event', 'work')
ORDER BY global_order DESC
type
是 Doctrine 添加的鉴别器列。虽然 WHERE
子句没有用,因为 type
始终是 IN
值之一,但我无法删除它。
EXPLAIN
告诉我
+------+---------------+------+------+-----------------------------+
| type | possible_keys | key | rows | Extra |
+------+---------------+------+------+-----------------------------+
| ALL | NULL | NULL | 562 | Using where; Using filesort |
+------+---------------+------+------+-----------------------------+
(rows
每次执行查询都不一样)
经过一些阅读,我发现我可以像这样强制使用索引:
ALTER TABLE `publication` DROP INDEX `UNIQ_AF3C6779B12CE9DB` ,
ADD UNIQUE `UNIQ_AF3C6779B12CE9DB` ( `global_order` , `type` )
和
SELECT *
FROM publication
FORCE INDEX(UNIQ_AF3C6779B12CE9DB)
WHERE global_order > 0
AND type IN ('article', 'event', 'work')
ORDER BY global_order DESC
WHERE
子句总是没用,但这次EXPLAIN
告诉我
+-------+-----------------------+-----------------------+------+-------------+
| type | possible_keys | key | rows | Extra |
+-------+-----------------------+-----------------------+------+-------------+
| range | UNIQ_AF3C6779B12CE9DB | UNIQ_AF3C6779B12CE9DB | 499 | Using where |
+-------+-----------------------+-----------------------+------+-------------+
在我看来它更好,但是强制索引似乎并不常见,所以我想知道对于这样一个简单的查询它是否真的有效。
有谁知道执行此查询的更好方法是什么?
谢谢!
最佳答案
如果您的查询确实是:
SELECT *
FROM publication
WHERE type IN ('article', 'event', 'work')
ORDER BY global_order DESC
... 并且所有条目(或几乎所有条目)都将与 IN 子句匹配,实际上没有索引会更好。如果您使用 limit 子句,那么您需要的索引实际上位于 global_order
上,没有 type
字段。这样做的原因是,读取索引实际上需要一些费用。
如果您要获取整个表格,顺序读取表格并在内存中对其行进行排序将是成本最低的方案。如果您只需要几行并且大多数行都匹配 where 子句,那么使用最小的索引就可以了。
要了解原因,请想象一下所涉及的磁盘 IO。
假设您想要没有索引的整个表。为此,您读取 data_page1、data_page2、data_page3 等,依次访问涉及的各个磁盘页面,直到到达表的末尾。然后您排序并返回。
如果您想要没有索引的前 5 行,您将像以前一样顺序读取整个表,同时对前 5 行进行堆排序。诚然,对于少数几行,这是大量的阅读和排序。
现在假设您想要整个表都有一个索引。为此,您需要依次读取 index_page1、index_page2 等。然后,这会引导您以完全随机的顺序(已排序的行在数据中出现的顺序)依次访问 data_page3、data_page1、data_page3、data_page2 等。所涉及的 IO 使得仅按顺序读取整个困惑并在内存中对抓包进行排序的成本更低。
相反,如果您只想要索引表的前 5 行,则使用索引成为正确的策略。在最坏的情况下,您会在内存中加载 5 个数据页并继续。
顺便说一下,一个好的 SQL 查询规划器会根据数据的碎片化程度来决定是否使用索引。如果按顺序获取行意味着在整个表中来回缩放,那么好的计划者可能会决定不值得使用索引。相比之下,如果表使用相同的索引进行集群,则保证行是有序的,从而增加了它被使用的可能性。
但是,如果你将同一个查询与另一个表连接起来,而另一个表有一个非常有选择性的 where 子句,可以使用一个小索引,规划器可能会决定它实际上更好,例如获取标记为 foo
的行的所有 ID,将它们与发布进行散列连接,然后在内存中对它们进行堆排序。
关于mysql - 为 ORDER BY 强制使用索引是否更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16144373/