【问题标题】:MySQL query performance?MySQL查询性能?
【发布时间】:2018-11-30 13:11:00
【问题描述】:

我有 MySQL 数据库和 5 个名为 tribes(groups)postsposts_to_groupspost_commentsposts_votes 的表。

组和帖子之间的关系是 MANY_2_MANY,因此每个帖子可以属于多个组,每个组可以包含 0-* 个帖子。这就是表 posts_to_groups 的作用。

我正在搜索从现在开始的过去 24 小时内发布到该用户关注的组中的 3 个最受欢迎的帖子(通过 posts_to_tribes - MANY_2_MANY 关系的表),并按 (cmets_count + votes_count 的总和) ) DESC

这是我当前的查询:

SELECT DISTINCT
    p.post_id,
    p.description,
    p.link,
    p.user_id,
    p.total_comments,
    p.total_votes,
    (SELECT 
            COUNT(*)
        FROM
            comments
        WHERE
            last_edited > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                AND post_id = p.post_id) AS comments_count,
    (SELECT 
            COUNT(*)
        FROM
            posts_votes
        WHERE
            date_voted > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                AND post_id = p.post_id) AS votes_count
FROM
    posts p
        JOIN
    posts_to_tribes pt ON pt.post_id = p.post_id
WHERE
    pt.tribe_id IN (3 , 38, 107)
ORDER BY (comments_count + votes_count) DESC , p.last_edited DESC
LIMIT 3;

这个查询非常慢,现在需要 ~500ms

有什么方法可以重写这个查询来提高性能?

更新:

解释输出:

Tim3880建议的查询:

SELECT 
    p.post_id,
    p.description,
    p.link,
    p.user_id,
    p.total_comments,
    p.total_votes,
    t.comments_count,
    t.votes_count
FROM posts p
JOIN (
    SELECT 
        p.post_id,
        (SELECT 
                COUNT(*)
            FROM
                comments
            WHERE
                last_edited > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                    AND post_id = p.post_id) AS comments_count,
        (SELECT 
                COUNT(*)
            FROM
                posts_votes
            WHERE
                date_voted > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                    AND post_id = p.post_id) AS votes_count
    FROM
        posts p
            JOIN
        posts_to_tribes pt ON pt.post_id = p.post_id
    WHERE
        pt.tribe_id IN (3 , 38, 107)
    ORDER BY (comments_count + votes_count) DESC , p.last_edited DESC
    LIMIT 3
) t
ON p.post_id = t.post_id
ORDER BY (t.comments_count + t.votes_count) DESC , p.last_edited DESC

现在需要 ~280 毫秒

解释输出:

【问题讨论】:

  • 请将您查询中EXPLAIN SELECT... 的结果与您的表定义一起添加。
  • EXPLAIN 输出是什么?您在WHERE 条件中使用的列上是否有索引?
  • 感谢您的 cmets !不幸的是,我现在无法访问此数据库,我将在明天提供此信息。
  • 没有执行计划,我们只能猜测。您可以尝试使用 group by post id 重写您的子查询,然后将它们加入外部查询。
  • 500 毫秒似乎并不特别慢,尤其是在基础表有大量数据的情况下。你的期望是什么。

标签: mysql sql


【解决方案1】:

如果您的 post_id 是主键(或唯一键),请尝试先获取 3 个 post_id:

SELECT 
    p.post_id,
    p.description,
    p.link,
    p.user_id,
    p.total_comments,
    p.total_votes,
    t.comments_count,
    t.votes_count
FROM posts p 
JOIN (
    SELECT 
        p.post_id,
        (SELECT 
                COUNT(*)
            FROM
                comments
            WHERE
                last_edited > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                    AND post_id = p.post_id) AS comments_count,
        (SELECT 
                COUNT(*)
            FROM
                posts_votes
            WHERE
                date_voted > DATE_SUB(NOW(), INTERVAL 24 HOUR)
                    AND post_id = p.post_id) AS votes_count
    FROM
        posts p
            JOIN
        posts_to_tribes pt ON pt.post_id = p.post_id
    WHERE
        pt.tribe_id IN (3 , 38, 107)
        AND p.last_edited >  DATE_SUB(NOW(), INTERVAL 24 HOUR)
    ORDER BY (comments_count + votes_count) DESC , p.last_edited DESC
    LIMIT 3
) t
ON p.post_id = t.post_id
ORDER BY (t.comments_count + t.votes_count) DESC , p.last_edited DESC

编辑:这是加入版本:

SELECT 
    p.post_id,
    p.description,
    p.link,
    p.user_id,
    p.total_comments,
    p.total_votes,
    t.comments_count,
    t.votes_count
FROM posts p 
JOIN (
    SELECT 
        p.post_id,Comments_Count, Votes_Count
    FROM
        posts p
        JOIN
        posts_to_tribes pt ON pt.post_id = p.post_id
        LEFT JOIN (SELECT 
                post_id, COUNT(*) Comments_Count
            FROM
                comments
            WHERE
                last_edited > DATE_SUB(NOW(), INTERVAL 24 HOUR)
            GROUP BY post_id) cc
        ON p.post_id = cc.post_id
        LEFT JOIN 
        ( 
            SELECT 
                post_id, COUNT(*) Votes_Count
            FROM
                posts_votes
            WHERE
                date_voted > DATE_SUB(NOW(), INTERVAL 24 HOUR)
            GROUP BY post_id
        ) vc
        ON p.post_id = vc.post_id
        WHERE pt.tribe_id IN (3 , 38, 107)
    ORDER BY (comments_count + votes_count) DESC , p.last_edited DESC
    LIMIT 3
) t
ON p.post_id = t.post_id
ORDER BY (t.comments_count + t.votes_count) DESC , p.last_edited DESC

如果性能仍然无法接受,您可能不得不考虑直接更新 total_cmets、total_votes 或使用触发器或计划作业。

【讨论】:

  • 谢谢,这是最快的查询.. 但它显示了与原始结果不同的其他输出结果..
  • 它的工作速度比原始查询快 2 倍。但速度仍然不完美。可能这里的相关子查询也可以优化?
  • 您的解释输出没有表明任何子查询问题。请发布您的新解释结果。
  • 为您的查询添加了解释输出
  • 我添加了“AND p.last_edited > DATE_SUB(NOW(), INTERVAL 24 HOUR)”这一行。在我的测试环境中,它有很大的不同。
【解决方案2】:

您已使用 2 个correlated subqueries。相关子查询中的每个查询都将对外部查询中的每一行执行一次。因此,如果您可以避免它们,则可能会获得更快的查询。

[..] 它们效率低下并且可能很慢。将查询重写为连接可能会提高性能。

你必须通过使用 join 来避免它们。这可能会对您有所帮助:MySQL - can I avoid these correlated / dependant subqueries?

【讨论】:

  • 谢谢,你能帮我解决这个问题吗?
猜你喜欢
  • 2011-05-06
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-13
  • 2016-03-09
相关资源
最近更新 更多