关于优化MySQL查询:简单的MySQL查询需要3秒以上

时间:2016-03-28 04:22:32

标签: mysql optimization query-optimization

据我所知,以下查询并不太复杂,但仍需要超过3秒。 关于如何优化这个的任何想法?

SELECT
    wpp.ID, wpp.post_title, wpp.post_author,
    wpp.post_status, s.supplier_company
FROM wp_posts AS wpp 
LEFT JOIN wp_postmeta AS postmeta ON wpp.ID = postmeta.post_id 
LEFT JOIN wp_term_relationships AS term_link ON wpp.ID = term_link.object_id 
LEFT JOIN wp_terms AS terms ON term_link.term_taxonomy_id = terms.term_id 
LEFT JOIN wp_teleapo_supplier AS s ON wpp.post_author = s.ID 
WHERE wpp.post_type = 'post'
AND wpp.post_warning <> 'no_image'
AND wpp.post_status <> 'trash'
AND wpp.post_status <> 'auto-draft'
GROUP BY wpp.ID 
ORDER BY post_date DESC 
LIMIT 100 OFFSET 0

现在我的所有表格大约有2000~9000个帖子 1)wp_posts中的15000条记录,WHERE wpp.post_type = 'post'只有大约3000条记录 2)wp_term_relationships中约9000左右 但是在不久的将来很容易成长......

问题的根源:

在上面的简短查询中,如果我改变:

GROUP BY wpp.ID 
ORDER BY post_date DESC 

GROUP BY wpp.ID 
ORDER BY wpp.ID DESC 

我的查询时间从3.3秒到0.3秒......但是我想要一种方式,所以我仍然可以ORDER BY post_date

甚至更奇怪的是,即使GROUP和ORDER BY不同,下面的完整搜索查询也会低于1秒......

简短查询的EXPLAIN结果:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  wpp ref type_status_date,post_type,post_status  post_type   62  const   3351    Using where; Using temporary; Using filesort
1   SIMPLE  postmeta    ref post_id post_id 8   r_11524_jtc.wpp.ID  7   Using index
1   SIMPLE  term_link   ref PRIMARY PRIMARY 8   r_11524_jtc.wpp.ID  92  Using index
1   SIMPLE  terms   eq_ref  PRIMARY PRIMARY 8   r_11524_jtc.term_link.term_taxonomy_id  1   Using index
1   SIMPLE  s   eq_ref  ID  ID  4   r_11524_jtc.wpp.post_author 1   

这些是我的指数:

enter image description here

完全搜索取决于用户是否填写了一些搜索字段,如下所示。最奇怪的是,即使ORDER BY列与GROUP BY列不同,下面的搜索查询也只需0.3秒!

SELECT  SQL_CALC_FOUND_ROWS wpp.ID, wpp.post_title, wpp.post_author,
        wpp.post_status, s.supplier_company,
        GROUP_CONCAT(DISTINCT terms.slug SEPARATOR ',') AS allslug,
        GROUP_CONCAT(DISTINCT terms.name SEPARATOR ',') AS allcatname
    FROM  wp_posts AS wpp
    LEFT JOIN  wp_postmeta AS postmeta ON wpp.ID = postmeta.post_id
    LEFT JOIN  wp_term_relationships AS term_link ON wpp.ID = term_link.object_id
    LEFT JOIN  wp_terms AS terms ON term_link.term_taxonomy_id = terms.term_id
    LEFT JOIN  wp_teleapo_supplier AS s ON wpp.post_author = s.ID
    WHERE  wpp.post_type = 'post'
      AND  wpp.post_warning <> 'no_image'
      AND  wpp.post_status <> 'trash'
      AND  wpp.post_status <> 'auto-draft' /* All search on post_title
      and  any postmeta value */
      AND  (post_title LIKE '%textile%'
        OR  postmeta.meta_value LIKE '%textile%')
              /* extra filters. The one below is an example of a filter on
                 the user #324 but this field can also take the username
                 (supplier_company) parameters etc. */
      AND  ( wpp.post_author LIKE '%324%'
              OR  ( EXISTS 
                      ( SELECT  1
                            FROM  wp_teleapo_supplier as s2
                            WHERE  s2.ID = wpp.post_author
                              AND  (s2.supplier_company      LIKE '%324%'
                                OR  s2.supplier_company_kana LIKE '%324%') ) )
              OR  ( EXISTS 
                      ( SELECT  1
                            FROM  wp_postmeta AS postmeta2
                            WHERE  postmeta2.post_id = wpp.ID
                              AND  postmeta2.meta_key = 'input_comp'
                              AND  postmeta2.meta_value LIKE '%324%' ) ) )
    GROUP BY  wpp.ID /* Filter on Categories!! */
    HAVING  ( allcatname LIKE '%apparel-and-accessories%'
             OR  allslug LIKE '%apparel-and-accessories%' )
    ORDER BY  post_date DESC
    LIMIT  20 OFFSET 0

1 个答案:

答案 0 :(得分:0)

想法A

潜在问题:如果没有GROUP BY,您是否看到一个supplier_company的多个wpp.ID值?如果是这样,你想要哪一个?或者你想要GROUP_CONCAT(s.supplier_company)

想法B

您希望NULL出现supplier_company吗?如果是这样,您可能会遇到这种缓慢的查询。 (并指责WP鼓励的EAV架构设计。)

如果您想跳过NULLs,请删除所有LEFTs。使用LEFT,查询将收集各种NULL,但最终只会丢弃它们。

想法C

此外,如果没有LEFTs,可能会以相反的顺序遍历表格。也就是说,从wp_teleapo_supplier开始并查看弹出的帖子。 (可能需要更多索引才能按顺序跟踪表。)

想法D

如果不需要GROUP BY,请将其删除并将INDEX(post_type)替换为INDEX(post_type, post_date)。这样,可能能够在仅找到100行时使用索引停止。或post_date不在wpp ??? JOINing时验证所有列。

其他

有些评论暗示你遗漏了一些信息。在那种情况下,我的建议可能毫无用处。请不要“简化”查询。

s没有PRIMARY KEY?这是禁忌。请提供SHOW CREATE TABLE wp_teleapo_supplier,以便我们讨论PK应该是什么。

更长的查询

在较长的查询中会出现许多性能杀手

  • LIKE '%...' - 无法使用索引
  • OR - 无法使用索引。一种解决方法是将查询重新表述为UNION,但考虑到多个ORs,这将是一场噩梦。
  • 不同列上的
  • GROUP BYORDER BY。即使它们位于相同的列中,也会在此查询中进行太多操作以获得更多好处。
  • OFFSET闻起来像“分页”。
  • SQL_CALC_FOUND_ROWS - 查询必须完成查找所有内容。由于LIMIT无法通过索引完成,因此无关紧要。

我能想到的唯一希望是在考虑搜索的情况下重新设计架构和UI。该查询没有明显的加速。