mysql - 关于优化MySQL查询: Simple MySQL query takes more than 3 seconds

标签 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

我所有的 table 现在大约有 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 秒内完成......

解释简短查询的结果:

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

最佳答案

想法A

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

想法B

您希望 supplier_company 出现 NULL 吗?如果是这样,您可能会陷入这个缓慢的查询。 (并归咎于 WP 鼓励的 EAV 架构设计。)

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

想法C

此外,如果没有LEFT,则可以按相反的顺序浏览表格。也就是说,从 wp_teleapo_supplier 开始,查看弹出哪些帖子。 (可能需要更多索引才能按该顺序遵循表格。)

想法D

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

其他

一些评论暗示您遗漏了一些信息。在这种情况下,我的建议可能就没用了。请不要“简化”查询。

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

更长的查询

较长的查询中会出现许多性能 killer

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

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

关于mysql - 关于优化MySQL查询: Simple MySQL query takes more than 3 seconds,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36255948/

相关文章:

php - 获取数字的类似excel的列名的算法

MySQL 获取时间优化

MySQL - 返回每个数据组的最后一条记录

mysql - mysql从多个表中获取数据,逗号分隔

php - 如何正确使用 Union 和 Order BY?

php - 将多个表行添加到mysql数据库中

optimization - 表访问 vs 函数调用 + 条件判断 : which is faster?

java - Proguard 的哪个优化选项删除了 if() 语句?

php - 有什么办法可以防止用户使用绝对路径看到私密图片吗?

mysql - 如何修复 flyway 的重大更改?