【问题标题】:MySQL query runs very slowMySQL查询运行很慢
【发布时间】:2020-02-20 10:59:02
【问题描述】:

我有一个永远运行的 MySQL (v5.7.26) 查询。这是查询:

SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (
        SELECT user_id
        FROM user_resource ur2
        WHERE ur2.action_date >= now() - INTERVAL 2 DAY
    )
    AND r.type = 'WORKOUT'
    GROUP BY ur.user_id;

通过尝试了解问题出在哪里,我已经玩了一点。出于测试目的,我尝试分成两个。所以:

SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY;

返回(很快)用户 user_id 的列表。 当我将返回的结果插入查询的第一部分时,如下所示:

SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (1,1,1,4,4,5,6,7,7,7);
      AND r.type = 'WORKOUT'
GROUP BY ur.user_id

它运行得非常快。我的假设是 IN(子查询)是瓶颈。

我正在考虑提取子查询并获取 user_ids,然后将其用作变量,但我不确定这是不是好方法,另外我遇到了问题。这是我的尝试:

-- first statement
SET @v1 = (SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)

-- second statement
SELECT
    ur.user_id      AS user_id,
    sum(r.duration) AS total_time,
    count(user_id)  AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE 
    ur.status = 1 
    AND NOT ur.action_date IS NULL 
    AND ur.user_id IN (@v1);
    AND r.type = 'WORKOUT'
GROUP BY ur.user_id

这里的问题是first statement返回错误:

子查询返回多于 1 行。

预期结果是 user_id,可以是重复的。我需要那些重复的。

我该如何解决这个问题?

【问题讨论】:

  • 我想知道为什么您的查询首先运行缓慢,尽管子查询很快并且替换值很快。您是否尝试将group by ur2.user_id 添加到您的子查询中?
  • 是的,我试过了。结果相同。 DB是RDS,大内存
  • AND NOT ur.action_date IS NULL 有点不和谐,为什么不 AND ur.action_date IS NOT NULL? (不要认为这与性能有任何关系,它只会冒犯我)
  • 请发布 DDL 和查询计划
  • INDEX(action_date, user_id) 可能会有所帮助。请为两张表提供SHOW CREATE TABLE

标签: mysql query-performance


【解决方案1】:

尝试EXISTS 而不是IN

...
AND EXISTS (SELECT *
                   FROM user_resource ur2
                   WHERE ur2.user_id = ur.user_id
                         AND ur2.action_date >= now() - INTERVAL 2 DAY)
...

以及user_resource (user_id, action_date)user_resource (status, action_date, user_id) 和/或user_resource (type) 上的索引。

【讨论】:

    【解决方案2】:

    你可以试试:

    -- first statement
    SET @v1 = (SELECT GROUP_CONCAT(user_id)
    FROM user_resource ur2
    WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
    
    -- second statement
    SELECT
    ur.user_id      AS user_id,
     sum(r.duration) AS total_time,
     count(user_id)  AS prefixes
    FROM user_resource ur
    INNER JOIN resource r ON r.id = ur.resource_id
    WHERE ur.status = 1 AND NOT ur.action_date IS NULL AND FIND_IN_SET(ur.user_id,@v1)
    AND r.type = 'WORKOUT'
    GROUP BY ur.user_id
    

    【讨论】:

      【解决方案3】:

      附加连接会比子查询更快:

      SELECT
          ur.user_id      AS user_id,
          sum(r.duration) AS total_time,
          count(user_id)  AS number_of_workouts
      FROM user_resource ur
      INNER JOIN resource r ON r.id = ur.resource_id
      INNER JOIN (
          SELECT user_id
          FROM user_resource ur2
          WHERE ur2.action_date >= now() - INTERVAL 2 DAY
      ) t ON t.user_id = ur.user_id
      WHERE 
          ur.status = 1 
          AND NOT ur.action_date IS NULL 
          AND r.type = 'WORKOUT'
          GROUP BY ur.user_id;
      

      【讨论】:

        猜你喜欢
        • 2013-01-01
        • 1970-01-01
        • 2013-08-14
        • 1970-01-01
        • 1970-01-01
        • 2018-01-23
        • 2014-01-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多