【问题标题】:mysql slow queries and timeout in wordpresswordpress中的mysql慢查询和超时
【发布时间】:2014-06-16 17:18:07
【问题描述】:

我不是 sql 专家。 我的 wordpress 开始返回超时并且响应非常慢。 当我开始挖掘时,我注意到 slow_query 日志有很多要告诉我的。 不幸的是,我有很多缓慢的查询。 例如:

# Time: 140425 17:03:29
# User@Host: geektime[geektime] @ localhost []
# Query_time: 7.024031  Lock_time: 0.000432 Rows_sent: 0  Rows_examined: 0
SET timestamp=1398434609;

SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
LEFT JOIN wp_postmeta AS order1 ON order1.post_id = wp_posts.ID
AND order1.meta_key = '_event_start_date'
LEFT JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'
WHERE 1=1
  AND wp_posts.post_type = 'event'
  AND (wp_posts.post_status = 'publish'
       OR wp_posts.post_status = 'future'
       OR wp_posts.post_status = 'draft'
       OR wp_posts.post_status = 'pending')
  AND ((wp_postmeta.meta_key = '_event_start_date'
        AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
       OR (mt1.meta_key = '_event_end_date'
           AND CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'))
GROUP BY wp_posts.ID
ORDER BY order1.meta_value,
         order2.meta_value ASC;

列 post_id、meta_id 和 meta_key 在 wp_postmeta 表中被索引。 列 ID、post_name、post_type、post_status、post_date、post_parent、post_author 和 guid 在 wp_posts 表中建立索引。

但是,列 ID 和 GUID 被索引了两次,是不是很糟糕?

并且有4个索引具有相同的key_name:type_status_date,是不是很糟糕?

为什么我在 wp_posts 中有 60K 行,而在 wp_postmeta 中有 3M 行?

我知道有很多问题要问,但我真的试图通过在线研究来理解。

提前致谢。

【问题讨论】:

  • 这个 WordPress 安装是否围绕一些专门的应用程序(如事件调度)?你里面有什么插件?
  • 不幸的是,我们使用了很多插件。但是对于上面的查询,您可能是对的,它可能与插件事件管理器有关。
  • 列的单独索引并不总是有用的。请参阅 this 以获取更好的索引 wp_metadata 的方法。

标签: mysql sql wordpress indexing


【解决方案1】:

但是,列 ID 和 GUID 被索引了两次,是不是很糟糕?

有两个不同的列,所以不,除非您的意思是它们都有 两个 索引 - 在这种情况下,是的,这很糟糕,并且可能是您的一个主题或插件中的一个错误(或 WP 本身的先前错误)。

并且有4个索引具有相同的key_name:type_status_date,是不是很糟糕?

同上:如果您指的是四个相同的索引,它要么是主题、插件或 WP 错误,您可以放心地删除重复项。

为什么我在 wp_posts 中有 60K 行,而在 wp_postmeta 中有 3M 行?

因为 WP 元 API 吸收并强制执行称为实体属性值(也称为 EAV)的数据库反模式:

http://en.wikipedia.org/wiki/Entity-attribute-value_model

粗略的谷歌搜索 SO 将产生大量线程来解释为什么如果这些东西需要出现在例如where、join 或 order by 子句。

您可以以您突出显示的慢查询的形式直接看到效率低下的情况。该查询四次加入元表,两次使用强制转换运算符引导 - 并且它将值转换为 char 而不是 date。雪上加霜,然后它继续使用存储在其中的值对行进行排序。这是导致性能不佳的秘诀。

遗憾的是,没有什么办法可以摆脱这种污水的令人厌恶的恶臭,除非编写自己的插件来创建适当的表来存储、索引和查询您需要的数据,而不是使用 WP 元 API,它可恶引用疯狂,以及使用它导致的腐烂 SQL。

当您从头开始重写您正在使用的插件时,您可以做的一件事是临时胶带和 WD-40 测量,就是在一个或多个过滤器上抛出回调您会在WP_Query#get_posts() 的类方法的巨大混乱中找到。例如,posts_request 过滤器包含完整的和最终的 SQL 查询,允许您使用 regex-foo 根据自己的喜好重写任何内容。这不是灵丹妙药:这样做可以修复诸如整数值按字典顺​​序排序等错误,以及偶尔进行查询优化;还有一点。

编辑:在重新阅读您的查询后,我认为您在最后一点方面很幸运。您的特定查询具有以下可憎之处:

INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
LEFT JOIN wp_postmeta AS order1 ON order1.post_id = wp_posts.ID
AND order1.meta_key = '_event_start_date'
LEFT JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'

其中两个具有共同的_event_start_date,因此您可以将其排除:

SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
       AND wp_postmeta.meta_key = '_event_start_date'
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
       AND mt1.meta_key = '_event_end_date'
INNER JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'
WHERE 1=1
  AND wp_posts.post_type = 'event'
  AND (wp_posts.post_status = 'publish'
       OR wp_posts.post_status = 'future'
       OR wp_posts.post_status = 'draft'
       OR wp_posts.post_status = 'pending')
  AND (CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'
       OR CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value,
         order2.meta_value ASC;

【讨论】:

    【解决方案2】:

    除其他外,使用以下函数会导致性能下降:

    AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
    

    假设该字段是日期字段,您将通过以下方式获得更好的性能:

     and wp_postmeta.meta_value >= AStartDateVariable 
     and wp_postmeta.meta_value < TheDayAfterAnEndDateVariable
    

    如果 meta_value 被索引,那就更是如此。我假设您会将这些变量作为查询参数发送。

    【讨论】:

    • 我比较了查询,它帮助了半秒,从 14 秒到 13 秒半。我也应该索引 meta_value 吗?
    • 索引会加快数据库读取速度,但会减慢数据库写入速度。因此你的困境。
    • @DanBracuk:遗憾的是,该字段存储为text,OP 无法控制...
    【解决方案3】:

    天哪! postmeta中的3兆行? 6万个帖子?您的安装出现严重问题。

    1. 您的事件表是否有可能对进入垃圾的垃圾邮件发送者开放?
    2. 您是否有大量旧的过期事件可以通过某种方式从您的系统中清除?

    您可以通过增加超时值来让您的系统重新启动。如果您知道如何处理php.ini,请查找超时值并增加它,或者向您的托管公司寻求帮助。

    您是每月 5 美元的托管公司之一吗?有六万个事件要处理,您可能需要升级。

    超时的近因是显而易见的。这段代码对那个怪物 post_meta 表进行了两次全扫描!

    为什么?它有一个 OR。它正在将函数应用于列的值。

    AND ((wp_postmeta.meta_key = '_event_start_date'
        AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
         OR (mt1.meta_key = '_event_end_date'
           AND CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'))
    

    当您扩展站点时,WordPress 架构的一个缺点是 postmeta 表的通用性。此查询会执行日期范围搜索,但很难索引像 postmeta 这样的键值存储库来优化这些。

    您了解您正在使用的事件管理器插件的代码吗?如果是这样,您可能想自己研究优化它。

    如果没有,请寻求事件管理器插件开发人员的支持。

    【讨论】:

    • 感谢您的见解。我有一个专用服务器。 60K 的帖子是合理的,因为我们真的是拥有 100 位作者的大型科技博客,但为什么帖子数量和 postmeta 之间的缺失相关性?我们没有垃圾邮件,大多数帖子都不是事件。
    • 当我删除“或”语句(事件结束日期)之后的行时,时间从 14 秒下降到 0.02 秒。
    • @Meschiany - 加速可能是由于缓存。明天再试。
    猜你喜欢
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 2012-12-15
    • 2019-09-28
    相关资源
    最近更新 更多