2017-03-31 15 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

ありがとうございます。私はインデックスを追加し、クエリは1秒未満で実行されます。 – 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_idのデータタイプと一致する必要があります。私はちょうど推測した。

post_idがポストテーブルの主キー(または一意のキー)であると仮定しています。

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

使用すると、操作が実行されていると、どのインデックスが使用されている実行計画を、見にEXPLAIN:

は、適切なインデックスが利用可能であることを確認してください。 (「Extra」列の「Using index for group」を参照してください)

関連する問題