SQL查询非常慢

时间:2017-03-31 16:01:11

标签: mysql sql

我有一个查询正在更新过去24小时内的观看次数和喜欢,但速度非常慢。对于大约3000个帖子,完成需要15秒以上,这是一个很大的问题,因为在此期间无法执行其他查询。

$update_query = mysqli_query($database, "UPDATE post SET 
                likes_last_day = (SELECT COUNT(post_like_id) FROM post_like WHERE post.post_id = post_like.post_id AND post_like.date > ('$current_date' - INTERVAL 1 DAY)), 
                views_last_day = (SELECT COUNT(post_view_id) FROM post_view WHERE post.post_id = post_view.post_id AND post_view.date > ('$current_date' - INTERVAL 1 DAY))");

有没有办法优化此查询以更快地执行?

3 个答案:

答案 0 :(得分:0)

这是您的查询:

UPDATE post p
    SET likes_last_day = (SELECT COUNT(*) FROM post_like l WHERE p.post_id = l.post_id AND l.date > ('$current_date' - INTERVAL 1 DAY)), 
        views_last_day = (SELECT COUNT(*) FROM post_view v WHERE p.post_id = v.post_id AND v.date > ('$current_date' - INTERVAL 1 DAY));

您正在使用子查询更新3000行。这需要一些时间。要加快查询速度,您可以使用索引:post_like(post_id, date)post_view(post_id, date)

您还可以在加入结果之前重写查询以进行聚合。但是,我怀疑使用正确的索引相关子查询实际上可能会更快。

答案 1 :(得分:0)

如果在添加索引后性能仍然存在问题,您可能会考虑在存储过程中执行此操作,因此db将重用执行计划,而不是为每个即席查询构建它。

答案 2 :(得分:0)

为了最小化锁定在post表上的时间,我们可以将结果预先聚合到临时表中,然后从中执行更新。

举个例子:

DROP TEMPORARY TABLE IF EXISTS `__post_last_day_counts__`
;
CREATE TEMPORARY TABLE `__post_last_day_counts__`
( post_id        BIGINT NOT NULL COMMENT 'pk'
, likes_last_day BIGINT NOT NULL
, views_last_day BIGINT NOT NULL
, PRIMARY KEY (post_id)
) ENGINE=InnoDB
;
INSERT INTO `__post_last_day_counts__`
( post_id
, likes_last_day
, views_last_day
)
SELECT p.post_id
     , IFNULL(lc.likes_last_day,0) AS likes_last_day
     , IFNULL(vc.views_last_day,0) AS views_last_day
  FROM post p
  LEFT
  JOIN ( SELECT pl.post_id
              , COUNT(pl.post_id) AS likes_last_day
           FROM post_like pl
          WHERE pl.date > ('$current_date' - INTERVAL 1 DAY)
       ) lc
    ON lc.post_id = p.post_id
  LEFT
  JOIN ( SELECT pv.post_id
              , COUNT(pv.post_id) AS views_last_day
           FROM post_view pv
          WHERE pv.date > ('$current_date' - INTERVAL 1 DAY)
       ) vc
    ON vc.post_id = p.post_id
;

使用临时表中的计数,我们可以更新帖子表...

UPDATE post t
  JOIN `__post_last_day_counts__` s
    ON s.post_id = t.id
   SET t.likes_last_day = s.likes_last_day
     , t.views_last_day = s.views_last_day
;

清理......

DROP TEMPORARY TABLE IF EXISTS `__post_last_day_counts__`
;

总的来说,这可能是较慢,但它可以最小化UPDATE语句持有锁定的时间。

注意:临时表中post_id列的数据类型应与post_id表中post的数据类型匹配。我只是猜了一下。

我们假设post_id是帖子表的主键(或唯一键)。

确保有合适的索引:

... on post_view (post_id, date) 
... on post_like (post_id, date)

使用EXPLAIN查看执行计划,正在执行哪些操作以及正在使用哪些索引。 (我们希望在Extra列中看到"在组和#34中使用索引。)