mysql在wordpress中慢查询和超时

时间:2014-04-30 11:42:56

标签: mysql sql wordpress indexing

我不是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行?

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

提前感谢。

3 个答案:

答案 0 :(得分:2)

  

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

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

  

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

与上述相同:如果您指的是四个相同的索引,它可能是主题或插件或WP错误,您可以安全地删除重复。

  

我怎么可能在wp_posts中有60K行,在wp_postmeta中有3M行?

因为WP meta API糟透了并强制执行称为实体属性值(也称为EAV)的数据库反模式:

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

粗略的谷歌搜索SO将产生大量的线程,解释为什么将数据存储在EAV或等效物(json,hstore,xml,无论如何)中是一个坏主意,如果这些东西需要出现在例如a where,join或order by clause。

您可以通过突出显示的慢查询的形式直接看到效率低下的问题。查询将加入元表四次,使用强制转换操作符进行两次引导 - 并将值转换为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;

答案 1 :(得分:0)

除此之外,使用这样的函数会导致性能下降:

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被编入索引,那将更加真实。我假设您将这些变量作为查询参数传送。

答案 2 :(得分:0)

圣牛! postmeta中的3个megarows? 60k帖子?您的安装出现严重问题。

  1. 您的事件表是否可能对垃圾邮件发送者进入垃圾邮件?
  2. 您是否有大量旧的过期事件可以以某种方式从您的系统中清除?
  3. 您可以通过增加超时值来重新启动系统。如果您知道如何处理php.ini,请找到超时值并增加它,或者向托管公司寻求帮助。

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

    超时的近因是显而易见的。这段代码全部扫描了那个怪物post_meta表TWICE!

    为什么呢?它有一个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这样的键值存储库编入索引以优化这些搜索。

    您是否了解自己使用的事件管理器插件代码的方法?如果是这样,您可能想要自己调查优化。

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