优化 WordPress 插件“更好的 WordPress 最近评论”中的慢查询

Posted

技术标签:

【中文标题】优化 WordPress 插件“更好的 WordPress 最近评论”中的慢查询【英文标题】:Optimize slow query in WordPress plugin "Better WordPress Recent Comments" 【发布时间】:2015-12-31 03:36:44 【问题描述】:

我正在优化针对 mysql 的查询,我的慢查询日志显示 WordPress 插件“更好的 WordPress 最近评论”小部件显示按帖子分组的最近 5 个 cmets,它使用 1.26 秒查询数据库,这是一个一个网站的时间很长 - 特别是当下一个网站只需点击一下。

这里是慢查询:

Query_time:1.265625 Lock_time:0.000000 Rows_sent:6 Rows_examined:288634

SET 时间戳=1443741678;

SELECT wpcoms.*
FROM (
SELECT *,
@num := if(@post_id = comment_post_ID, @num + 1, 1) as row_number,
@post_id := comment_post_ID as cpID
FROM wp_comments
WHERE comment_approved = 1 ORDER BY comment_post_ID DESC,
comment_ID DESC
) as wpcoms
WHERE wpcoms.row_number <= 2
ORDER BY wpcoms.comment_date DESC
LIMIT 6;

检查的行数为 288.634,但我的数据库仅包含 96.000 cmets。这当然应该可以改进,以便在很短的时间内实际上只检查几个 cmets,因为它显示的只有少数最近发布的 cmets。谢谢。

【问题讨论】:

这两个 order by 语句对我来说是可疑的,并确保您的 comment_approved 和 comment_approved 字段已编入索引。你可以在没有子选择的情况下做到这一点吗?为什么需要row_number?似乎子选择中的唯一点是产生 row_number 如果是这种情况(codex.wordpress.org/Transients_API),则将结果放入transient 缓存中。还要确保sql_calc_found_rows 在查询期间不存在,因为这将在很大程度上影响性能。 【参考方案1】:

我看到的主要问题之一是内部查询 SELECT *, @num...,因为它会导致全表扫描,这将导致 MySQL 不使用 comment_approved 索引。

这样做的原因很简单,因为如果您查看查询,它实际上是在告诉 MySQL 选择表中按帖子 ID 排序的每一行,没有限制。

mysql> explain SELECT *, @post_id := comment_post_ID as cpID FROM wp_comments WHERE comment_approved = 1 ORDER BY comment_post_ID DESC, comment_ID DESC LIMIT 10;
+----+-------------+-------------+------+---------------------------+------+---------+------+------+-----------------------------+
| id | select_type | table       | type | possible_keys             | key  | key_len | ref  | rows | Extra                       |
+----+-------------+-------------+------+---------------------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | wp_comments | ALL  | comment_approved_date_gmt | NULL | NULL    | NULL |  567 | Using where; Using filesort |
+----+-------------+-------------+------+---------------------------+------+---------+------+------+-----------------------------+

问题的另一部分是comment_post_ID 的排序

考虑一下这将使用索引:

mysql> explain SELECT *, @post_id := comment_post_ID as cpID FROM wp_comments WHERE comment_approved = 1 ORDER BY comment_date_gmt DESC LIMIT 10;
+----+-------------+-------------+-------+---------------------------+------------------+---------+------+------+-------------+
| id | select_type | table       | type  | possible_keys             | key              | key_len | ref  | rows | Extra       |
+----+-------------+-------------+-------+---------------------------+------------------+---------+------+------+-------------+
|  1 | SIMPLE      | wp_comments | index | comment_approved_date_gmt | comment_date_gmt | 8       | NULL |   10 | Using where |
+----+-------------+-------------+-------+---------------------------+------------------+---------+------+------+-------------+

后一种查询可能会影响您的结果,但效率会高得多。

更好的解决方案可能是 MySQL 查询和 php 过滤的某种组合以获得所需的结果。

由于这是一个插件,您可以考虑将其作为插件开发人员的问题打开,因为它会导致任何具有大量 cmets 的网站的性能不佳。

此答案绝不是解决方案,但希望为您指明正确的方向。当涉及到一个包含大量 cmets 的站点时,需要真正重新考虑查询和处理的逻辑。

【讨论】:

我尝试了您的建议,只需在“ORDER BY comment_post_ID DESC, comment_ID DESC”中加入 LIMIT 100,查询时间就从 1.265 秒提高到了 0.125 秒。通过将 ORDER BY 字段更改为“comment_date_gmt”以及您的完整建议 LIMIT 100,将查询改进为 0.0000 秒。大奖!非常感谢您,draw010!我会通知插件的开发者更改他的代码。我已经用你的新语句更新了 .php 文件,它运行完美! :-) @Frank 我很高兴您能够根据我的建议更改查询并获得所需的结果,同时看到巨大的性能提升。很高兴为您提供帮助!【参考方案2】:

在没有看到实际数据并进行有根据的猜测的情况下,您正在寻找最近的“已批准”帖子,然后希望获得每个帖子最近的 2 个 cmets。整体查询还可以,我只是更进一步,试一试。

SELECT 
      wpcoms.*
   FROM 
      ( SELECT 
              *,
              @num := if(@post_id = c1.comment_post_ID, @num + 1, 1) as row_number,
              @post_id := c1.comment_post_ID as cpID
           FROM 
              ( select distinct c2.comment_post_id
                   from wp_comments c2
                   where c2.comment_approved = 1
                   order by c2.comment_post_id desc
                   limit 6 ) Just6
                   JOIN wp_comments c1
                   ON Just6.comment_post_id = c1.comment_post_id
           WHERE 
              c1.comment_approved = 1 
           ORDER BY 
              c1.comment_post_ID DESC,
              c1.comment_ID DESC
      ) as wpcoms
   WHERE 
      wpcoms.row_number <= 2
   ORDER BY 
      wpcoms.comment_date DESC
   LIMIT 6;

唯一真正的调整是有一个内部子查询,该查询仅对具有已批准评论 DISTINCT PER COMMENT_POST_ID 的评论帖子 ID 值进行预限定,并将其限制为仅 6 条记录。通过这样做,并在这些合格的comment_post_id 上重新加入到 cmets 表中,您不会查询所有可能的帖子,而只是查询合格的 6 个帖子。从那时起,这些 cmets 被减少到每个评论帖子最多只有 2 个,然后最终确定您的列表总共有 6 个条目。但是,如果您总是想要 6 个评论帖子,请摆脱外部限制 6。这样,您最多可以拥有 12 个条目,这对于 6 个内部限定的评论帖子 ID 值来说是 2 个可能的条目。

现在,不知道您的索引,我建议您在 wp_cmets 表上使用以下两个复合索引。但至少索引 #1

1.  ( comment_approved, comment_post_id )
2.  ( comment_post_id, comment_id )

【讨论】:

以上是关于优化 WordPress 插件“更好的 WordPress 最近评论”中的慢查询的主要内容,如果未能解决你的问题,请参考以下文章

优化提速,6款热门WordPress缓存插件推荐

Wordpress插件过多很臃肿,怎么优化

支持中文的WordPress缓存加速优化插件WP Fastest Cache

适合wordpress中文网站的seo优化插件 DX-Seo

WordPress的SEO插件——WordPress SEO by yoast安装及使用

wordpress 插件是啥意思