2013-04-22 80 views
0

我目前正在努力通过最优化原则2在该表上生成一个查询:为ORDER BY强制索引使用是否更好?

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是学说增加了鉴别列。尽管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 | 
+-------+-----------------------+-----------------------+------+-------------+ 

在我看来,这是更好的,但现在看来,这不是常见的有给力的指数也因此我不知道它是否真的有效对于这样一个简单的查询。

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

谢谢!

回答

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等。这会导致您以完全随机的顺序(数据中出现排序的行)访问数据页面3,数据页面1,数据页面3和数据页面2等。所涉及的IO使得按顺序读取整个混乱并在存储器中分类抓包变得更便宜。

如果您只想索引表的前5行,相反,使用索引将成为正确的策略。在最糟糕的情况下,您会在内存中加载5个数据页并继续前进。

一个很好的SQL查询规划器btw会根据您的数据是如何分割来决定是否使用索引。如果按顺序提取行意味着在整个表格中来回缩放,优秀的规划师可能会认为不值得使用索引。相比之下,如果表使用相同的索引进行聚类,那么这些行保证按顺序排列,从而增加它被使用的可能性。但是,如果您使用另一个表格连接相同的查询,并且其他表格具有极其有选择性的可以使用小索引的where子句,则规划人员可能会认为它实际上更好,例如,获取标记为foo的行的所有ID,将其与发布进行散列连接,并将堆在存储器中进行排序。

+0

优秀的,详细的答案。 +1 – eggyal 2013-04-25 08:23:15

+0

哇,谢谢你的解释! – MatTheCat 2013-04-25 09:05:44

+0

Woops,我认为赏金是在答案被选中时给出的,对不起^^' – MatTheCat 2013-04-28 19:40:23

1

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

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

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

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

+0

您可以使用SQL_NO_CACHE修饰符来SELECT,以确保不会从缓存中检索结果集。 – fenway 2013-04-25 02:41:57

+0

我无法看到任何有意义的区别。也许5000行是不够的,但无论如何,我很确定我不会有那么多!谢谢。 – MatTheCat 2013-04-25 09:05:22

0

强制使用索引很少是最好的答案。一般来说,最好创建和/或优化索引(索引),以便MySQL选择使用它们。 (这是更好的优化查询,但我明白你不能这样做。)

当你使用类似Doctrine的东西,你不能优化查询和索引没有帮助,你最好的选择是专注于查询缓存。 :-)