我们如何在 MySQL 5.5 中快速进行此查询(按多列分组 + 排序,具有 2 个左连接)?
Posted
技术标签:
【中文标题】我们如何在 MySQL 5.5 中快速进行此查询(按多列分组 + 排序,具有 2 个左连接)?【英文标题】:How do we make this query fast in MySQL 5.5 (group by + order by multiple columns with 2 left joins)? 【发布时间】:2014-03-16 05:54:16 【问题描述】:表类型:InnoDB mysql 5.5 (Debian 7.0)
我们有这个查询:
SELECT SQL_NO_CACHE t.*, count(ul.user_id) AS like_count FROM post p
force index (added_utc_date_and_time_sort_idx)
LEFT OUTER JOIN user_post_liked ul ON p.id = ul.post_id
LEFT OUTER JOIN user_post u ON u.post_id = p.id
GROUP BY p.id
ORDER BY p.added_utc_date ASC, p.added_utc_time ASC, p.hash ASC LIMIT 0,10;
有索引 added_utc_date_and_time_sort_idx(p.added_utc_date, p.added_utc_time, p.hash), primary(post.id)
EXPLAIN 显示了一个临时表和文件排序,查询大约需要 4 秒,只有 20K 行/200MB 数据(非常慢,因为我们将有 200 万行以上 == 400+ 秒查询时间):
id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra 1, SIMPLE, p, ALL, NULL, NULL, NULL, NULL, 24576, "使用临时;使用文件排序" 1, SIMPLE, ul, ref, PRIMARY, PRIMARY, 764, posta.p.id,1,"使用索引" 1, SIMPLE, u, ref, fk_user_post_idx, fk_user_post_idx, 764, posta.p.id,1,"使用索引"现在我们想要 mysql 使用索引对行进行排序,而不是进行文件排序,因为我们只读取前 10 个结果。
【问题讨论】:
您可能只读取了查询的前 10 个结果行,但 mysql 仍然必须对所有 2+ 百万行进行排序才能确定前 10 行应该是什么。 我们添加了一个多列索引,专门针对这个查询的 order by 子句,这样 mysql 就不需要进行排序了。 强制使用索引是个坏主意。您正试图超越查询优化器。不推荐。 我们最初尝试了没有强制索引的查询,如下所示,但由于查询花费了很长时间,我们尝试对其强制索引。请注意,EXPLAIN 在两种情况下都给了我们相同的执行计划(有和没有强制索引)。如果试图让 MySQL 使用索引不是正确的方向,我们渴望尝试任何其他可以建议的方法来加快这个查询。 【参考方案1】:您的问题是GROUP BY
与ORDER BY
的组合。没有一个索引可以涵盖两者!
要使其在不排序的情况下工作,您需要 一个 索引满足 GROUP BY
(p.id
) 所需的顺序以及 ORDER BY
( p.added_utc_date ASC, p.added_utc_time ASC, p.hash ASC
)。这两个顺序要求不共享一个公共前缀,因此您不能有一个支持两者的单一索引。
但是,如果您可以使用ORDER BY p.id, p.added_utc_date, ...
,那么您可以为此创建一个索引,并且它应该适用于两者(我仍然不知道 MySQL 是否足够聪明!)。
长话短说:您正在考虑的执行计划对于您的查询来说是不可能的。
参考资料:
IndexedORDER BY
Indexed GROUP BY
【讨论】:
在我们添加第二个索引(id、日期、时间、哈希)后,MySQL 能够使用索引来评估查询。以上是关于我们如何在 MySQL 5.5 中快速进行此查询(按多列分组 + 排序,具有 2 个左连接)?的主要内容,如果未能解决你的问题,请参考以下文章