强制ORDER BY的索引使用是否更好?

时间:2013-04-22 10:04:34

标签: mysql indexing sql-order-by range filesort

我目前正在尝试优化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 |
+-------+-----------------------+-----------------------+------+-------------+

在我看来它更好,但似乎强制索引也不常见,所以我想知道这对于这样一个简单的查询是否真的有效。

有谁知道执行此查询的更好方法是什么?

谢谢!

3 个答案:

答案 0 :(得分:4)

如果您的查询确实是:

SELECT *
FROM publication
WHERE type IN ('article', 'event', 'work')
ORDER BY global_order DESC

...并且所有条目(或几乎所有条目)都匹配IN子句,实际上你没有索引就更好了。如果您输入限制条款,那么您需要的索引实际上位于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查询计划程序btw将根据数据的碎片程度决定是否使用索引。如果按顺序获取行意味着在表格中来回缩放,那么一个好的计划者可能会认为使用索引是不值得的。相反,如果使用相同的索引对表进行聚类,则保证行按顺序排列,从而增加了它的使用可能性。

但是,如果你用另一个表加入相同的查询,而另一个表有一个非常有选择性的where子句可以使用一个小索引,那么规划者可能会认为它实际上更好,例如获取标记为foo的所有行的ID,使用发布散列它们,并在内存中对它们进行堆排序。

答案 1 :(得分:1)

MySQL尝试确定运行给定查询的最佳方式,并根据其认为最佳的方式决定是否使用索引。

这并不总是正确的。有时手动强制查询使用索引更快,有时则不然。

如果您在特定情况下使用示例数据运行某些测试,您应该能够看到哪种方法执行得更快,并坚持使用该方法。

确保您考虑查询缓存以获得准确的效果基准。

答案 2 :(得分:0)

强制使用索引很少是最好的答案。通常,最好创建和/或优化索引(索引),以便MySQL选择使用它们。 (优化查询更好,但我知道你不能在这里做到这一点。)

当您使用像Doctrine这样无法优化查询且索引无效的内容时,最好的办法是专注于查询缓存。 : - )