2017-03-31 329 views
0

我有一個查詢過去24小時更新視圖和喜歡的帖子,但速度很慢。對於大約3000個帖子,完成需要15秒以上,這是一個很大的問題,因爲在此期間無法執行其他查詢。SQL查詢速度很慢

$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))"); 

有沒有一種方法來優化此查詢以更快地執行?

+0

您是否對這些列有索引? –

+1

在phpMyAdmin中運行查詢並在開始時(即在UPDATE之前)添加EXPLAIN。用這個輸出更新你的問題.. – Chris

回答

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)

在加入結果之前,您也可以重寫查詢來完成聚合。但是,我懷疑相關的子查詢實際上可能使用正確的索引更快。

+0

謝謝。我添加了索引,並在一秒鐘內執行查詢。 – user3593157

0

如果在添加索引後性能仍然是一個問題,您可能會考慮在存儲過程中執行此操作,因此db將重用執行計劃,而不是爲每個即席查詢構建它。

0

爲了最大限度地減少鎖定在帖子表上的時間,我們可以將結果預先聚合到一個臨時表中,然後從中執行更新。

舉個例子:

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__` 
; 

總的來說,這可能是較慢的,但它可能會最小化時間量t UPDATE語句包含鎖。

注意:臨時表中的post_id列的數據類型應與post表中的post_id的數據類型匹配。我只是猜測。

我們假設post_id是張貼表的主鍵(或唯一鍵)。

確保選中合適的指標可供選擇:

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

使用EXPLAIN看執行計劃,目前正在執行的操作和正在使用的索引。 (我們希望在Extra列中看到「在組中使用索引」。)