mysql - 为 ORDER BY 强制使用索引是否更好?

标签 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 |
+-------+-----------------------+-----------------------+------+-------------+

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

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

谢谢!

最佳答案

如果您的查询确实是:

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/

相关文章:

sql - 按 desc 排序顺序未按预期工作

java - SQL 按同一列的两组条件排序

mysql - Sqoop 从 mysql 导入空值不会替换 Null

php - 警告 : mysqli_free_result() again

'on clause' 中的 MySQL 未知列

algorithm - 获得特定的替换组合

c# - Elasticsearch C# NEST IndexMany child

mysql - 我如何确保此查询坚持索引?

MySQL顺序字母数字列

mysql最近邻搜索