4

友達が最も閲覧したページを表示できる機能を提供しようとしています。friends テーブルには 570 万行、views テーブルには 530 万行あります。現時点では、これら 2 つのテーブルに対してクエリを実行し、ある人の友人によって最も多く閲覧された 20 のページ ID を見つけたいだけです。

私が今持っているクエリは次のとおりです。

SELECT page_id 
FROM `views` INNER JOIN `friendships` ON friendships.receiver_id = views.user_id 
WHERE (`friendships`.`creator_id` = 143416) 
GROUP BY page_id 
ORDER BY count(views.user_id) desc 
LIMIT 20

そして、説明は次のようになります。

+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
| id | select_type | table       | type | possible_keys                           | key                             | key_len | ref                                     | rows | Extra                                        |
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | friendships | ref  | PRIMARY,index_friendships_on_creator_id | index_friendships_on_creator_id | 4       | const                                   |  271 | Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | views       | ref  | PRIMARY                                 | PRIMARY                         | 4       | friendships.receiver_id                 |   11 | Using index                                  | 
+----+-------------+-------------+------+-----------------------------------------+---------------------------------+---------+-----------------------------------------+------+----------------------------------------------+

ビュー テーブルには (user_id, page_id) の主キーがあり、これが使用されていることがわかります。友情テーブルには、(receiver_id、creator_id) のプライマリ キーと、(creator_id) のセカンダリ インデックスがあります。

group by と limit を指定せずにこのクエリを実行すると、この特定のユーザーに対して約 25,000 行になります。これは一般的な値です。

最新の実際の実行では、このクエリの実行に 7 秒かかりました。これは、Web アプリで適切な応答を得るには長すぎます。

私が疑問に思っていることの 1 つは、セカンダリ インデックスを (creator_id、receiver_id) に調整する必要があるかどうかです。ただし、それによってパフォーマンスが大幅に向上するかどうかはわかりません。この質問への回答に応じて、今日試してみます。

クエリを書き直して非常に高速にする方法がわかりますか?

更新:さらにテストを行う必要がありますが、データベースでグループ化とソートを行わず、後でルビーで行うと、私の厄介なクエリがうまく機能するようです。全体的な時間ははるかに短く、約 80% 短縮されているようです。おそらく私の初期のテストには欠陥があったのでしょう - しかしこれは間違いなくもっと調査する必要があります. それが本当なら、wtf は Mysql を実行していますか?

4

3 に答える 3

1

私の知る限り、そのような「電光石火」のようなクエリを作成する最善の方法は、クリエーターごとのページごとにフレンド ページ ビューを追跡するサマリー テーブルを作成することです。

おそらく、トリガーを使用して最新の状態に保ちたいと思うでしょう。その後、集計はすでに完了しており、最も閲覧されたページを取得するための単純なクエリです。集計テーブルに適切なインデックスがあることを確認して、データベースが最も多くのビューを取得するために並べ替える必要さえないようにすることができます。

サマリー テーブルは、読み取りがほとんどの環境で集計タイプのクエリのパフォーマンスを良好に維持するための鍵となります。更新が発生したとき (まれ) に前もって作業を行い、その後 (頻繁に) クエリを実行する必要がありません。

統計が完璧である必要がなく、書き込みが実際にかなり頻繁に行われる場合 (おそらくページ ビューなどの場合)、ビューをメモリにまとめてバックグラウンドで処理することができます。ページを表示する際に、集計テーブルを最新の状態に保つ必要はありません。このソリューションは、データベースの競合も減らします (要約テーブルを更新するプロセスが少なくなります)。

于 2009-06-17T01:46:53.377 に答える
0

このテーブルの非正規化を絶対に検討する必要があります。ユーザー ID と、ユーザーが閲覧したすべてのページの正確な数を保持する別のテーブルを作成すると、クエリがはるかに簡単になります。

ビューテーブルでトリガーを使用すると、このテーブルを簡単に維持できます。これにより、「ビュー」テーブルで挿入が発生するたびに「views_summary」テーブルが更新されます。

実際の関係を調べたり、1 人あたりの上位 x ページを維持したりすることで、これをさらに非正規化することもできるかもしれません

お役に立てれば、

エバート

于 2009-06-17T01:49:00.003 に答える