【问题标题】:MySQL JOIN / IN performance optimizationMySQL JOIN/IN 性能优化
【发布时间】:2015-06-16 15:38:31
【问题描述】:

我有以下 MySQL 查询:

SELECT 
    p.post_id,
    p.date_created,
    p.description, 
    p.last_edited, 
    p.link, 
    p.link_description, 
    p.link_image_url, 
    p.link_title, 
    p.total_comments, 
    p.total_votes, 
    p.type_id, 
    p.user_id 
FROM posts p JOIN posts_to_tribes ptt ON p.post_id=ptt.post_id 
WHERE ptt.tribe_id IN (1, 2, 3, 4, 5) 
GROUP BY p.post_id 
ORDER BY p.last_edited DESC, p.total_votes DESC LIMIT 25

在非并发环境中,此查询运行约 172 毫秒,但在并发环境中运行 1-2 秒(在性能测试期间)。

解释输出:

posts_to_tribes 表上的索引:

有什么办法可以提高性能吗?

【问题讨论】:

  • ptt.tribe_id 上可能有索引
  • 基本经验法则:在“决策”上下文(join、where、order by)中使用的任何字段都应该有一个索引。
  • 感谢您的回答,我在 posts_to_tribes.tribe_id 上添加了一个索引,但没有任何改变.. 查询现在运行约 188 毫秒.. 可能是我做错了什么..
  • 为什么172ms的执行时间可以接受,但1-2秒的执行时间是不可接受的?
  • 我在这个功能上有 REST 端点。现在它在性能测试期间在并发环境中运行约 6 秒,而其他端点运行约 2-3 秒

标签: mysql sql performance


【解决方案1】:

您需要posts_to_tribes 的复合索引:INDEX(tribe_id, post_id)

GROUP BY 是为了弥补 JOIN 爆炸的行数。这是比IN ( SELECT ... ) 更好的解决方法:

SELECT  p.post_id, p.date_created, p.description, p.last_edited,
        p.link, p.link_description, p.link_image_url, p.link_title,
        p.total_comments, p.total_votes, p.type_id, p.user_id
    FROM  posts p
    JOIN  
      ( SELECT  DISTINCT  post_id
            FROM  posts_to_tribes
            WHERE  tribe_id IN (1, 2, 3, 4, 5)
      ) AS ptt USING (post_id)
    ORDER BY  p.last_edited DESC,
              p.total_votes DESC
    LIMIT  25

【讨论】:

  • 非常感谢!此查询运行约 140 毫秒
  • 还有一个问题,我们需要 p.last_edited 和 p.total_votes 的索引吗?
  • 没有。我不相信INDEX(last_edited, total_votes)(复合,按此顺序)会有任何好处。 (1) 它将从子查询开始,它不会让它到达那个索引。 (2) 这些听起来像是会发生很大变化的列,因此会产生UPDATE 的开销。你会被“文件排序”困住。
  • 我错过了什么?此查询将返回重复的行...?
  • 糟糕——将DISTINCT 添加到我的子查询中。
【解决方案2】:

当您真的想在两个表之间应用semi-join 时,您应用了JOIN 操作(SQL 中的半联接是使用INEXISTS 谓词实现的)。

因为您使用了错误类型的JOIN,所以您又使用GROUP BY 删除了重复记录。那里浪费了很多 CPU 周期。

下面的查询会快很多:

SELECT 
    p.post_id,
    p.date_created,
    p.description, 
    p.last_edited, 
    p.link, 
    p.link_description, 
    p.link_image_url, 
    p.link_title, 
    p.total_comments, 
    p.total_votes, 
    p.type_id, 
    p.user_id 
FROM posts p 
WHERE p.post_id IN (
  SELECT ptt.post_id
  FROM posts_to_tribes ptt
  WHERE ptt.tribe_id IN (1, 2, 3, 4, 5)
)
ORDER BY p.last_edited DESC, p.total_votes DESC LIMIT 25

(p.post_id)(ptt.tribe_id, ptt.post_id) 上仍应有索引

【讨论】:

  • 谢谢,现在这个查询运行~156ms 另外,我会在并发环境中的性能测试中检查它
  • 您是否按照我的指示在ptt 的两列上都设置了索引?
  • 在posts_to_tribes我有一个PK(tribe_id,post_id)。同样在帖子表中,post_id 也是 PK。我应该在这些字段上添加单独的索引吗?
  • @alexanoid:啊哈,好的。没有PK应该就足够了。对不起,我的 MySQL 知识太有限了。我敢肯定这会优化您在 Oracle 中的查询,不过...
猜你喜欢
  • 1970-01-01
  • 2016-03-16
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-18
  • 2013-08-07
  • 1970-01-01
相关资源
最近更新 更多