如何优化这个非常慢的 MySQL 查询?

Posted

技术标签:

【中文标题】如何优化这个非常慢的 MySQL 查询?【英文标题】:How can I optimize this VERY slow MySQL query? 【发布时间】:2014-07-04 04:29:06 【问题描述】:

我有一个基于 WordPress 的网站,它为区域科学期刊编目学术文章。

简单地说,系统有几千个“帖子”,每个都有一个“pub_type”分类法,其中只选择了一个术语:“手稿”,或其他。

每个帖子还有与其相关的各种其他分类法/术语。


目标:获取特定分类法的术语列表。对于每个术语,计算与其相关的帖子数,并确定其中有多少帖子在“pub_type”分类中设置了“手稿”。


当前查询:

SELECT term_id, term_id as term_id_b, name, slug,
( SELECT COUNT(id) FROM wp_posts WHERE id IN 
    ( SELECT object_id FROM wp_term_relationships WHERE term_taxonomy_id IN 
        ( SELECT term_taxonomy_id FROM wp_term_taxonomy WHERE term_id = term_id_b ) 
) AND post_status = "publish" ) as count,
( SELECT COUNT(id) FROM wp_posts WHERE id IN 
    ( SELECT object_id FROM wp_term_relationships WHERE term_taxonomy_id IN 
        ( SELECT term_taxonomy_id FROM wp_term_taxonomy WHERE term_id IN 
            ( SELECT term_id FROM wp_terms WHERE term_id = term_id_b ) 
        )
    ) 
AND id IN 
( SELECT object_id FROM wp_term_relationships WHERE term_taxonomy_id IN
    ( SELECT term_taxonomy_id FROM wp_term_taxonomy WHERE term_id = 
        ( SELECT term_id FROM wp_terms WHERE name = "Manuscript" ) 
    AND taxonomy = "pub_type" )
)
AND post_status = "publish"
) as manuscript_count
FROM wp_terms 
WHERE term_id IN 
( SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = "'.$taxonomy.'" )
ORDER BY name ASC

虽然此查询确实有效,但它的运行速度非常慢...根据服务器负载在 3-5 分钟之间。太糟糕了,为了保持网站性能,我不得不将查询结果缓存到 JSON 文本文件中,并且只让查询每 2 小时运行一次。

我知道这里的主要问题是我对所有内容都使用了子查询。虽然我正在尝试了解有关使用联接的更多信息,但我还不够了解以任何其他方式编写此查询。

谁能就我如何驯服这只野兽提供一些见解或建议?

编辑:这是查询的 EXPLAIN 输出的屏幕截图:

http://i.imgur.com/Axaqun3.png

【问题讨论】:

很多时候子查询,WHERE EXISTSWHERE IN 快​​。但是,为了给您一个更好的答案,我需要查看查询计划。您能否将EXPLAIN PLAN 输出添加到您的问题中? 如果我想去那里,我不会从这里开始。 :-( 考虑提供适当的 DDL(和/或 sqlfiddle)以及所需的结果集。 你是说你想这样做:计算每个词条为 pub_type 分类法的帖子数? 【参考方案1】:

请参阅this great post 了解IN 的使用以及它如何对查询速度产生负面影响。上面写满了你的问题。

本质上,使用传统的IN(values) 查询,您只需搜索每个值。在子查询 IN 中,就像你一样 (IN(SELECT))。

其中,作者引用了 mysql 的手册:

如果内部和外部查询分别返回 M 和 N 行,则执行时间将变为 O(MxN) 的顺序,而不是 O(M+N),因为对于不相关的子查询。

使用此逻辑,您将wp_posts 作为外部查询,并在其中嵌套wp_terms_relationship 10 行,并在 that 内再次嵌套wp_terms。那个是949*(10*1))。第二次你只有两层嵌套,但因为最后一层只有一行,所以仍然是同样的影响:你最终解析了 9490 行。

子查询不是你的朋友。虽然有时它们可​​能是必要的,但它们几乎总是可以使用JOIN 来解决。根据您的目标,这似乎没有太大不同。通过重写此查询并扩展以尝试使用 JOIN...ON,您会发现经过短暂的学习曲线后,阅读您自己的代码会更容易遵循其逻辑进展,并且很可能,看到它也加快了。至少,尽可能使用joins。以后你会感谢自己的。

【讨论】:

以上是关于如何优化这个非常慢的 MySQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

记一次mysql查询慢的优化历程

如何优化这个 Postgres 查询?

你将如何优化这个简短但非常慢的 Python 循环?

MySQL中like查询速度慢的问题

如何优化我的 SQL 查询?

如何优化这个嵌套的 SQL SELECT 查询