膨大な数の投稿があり、mysql がメモリ内でテーブルの並べ替えを実行できなくなり、インデックスを十分に活用していないように見える非常に活発なフォーラムのパフォーマンスを改善する簡単な方法を見つけようとしています。
この単純なクエリは、各トピックの最新の投稿を見つけて、ユーザーがそれ以降に返信があるかどうかを判断します (後で topic_time を比較することにより)。
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p
WHERE p.poster_id = '1' AND p.post_status = '0'
GROUP BY p.topic_id
ORDER BY post_time DESC
LIMIT 50
シンプルでフラットなテーブルは次のようになります
post_id | poster_id | topic_id | post_status | post_time | post_text
ただし、100 万件の投稿があり、ユーザー自身が数万件の投稿を行うと、パフォーマンスが低下します。MySQL がメモリ内のテーブルをソートできなくなったか、スキャンする行が多すぎます。実際の使用では最大 3 秒かかることがあります。これは、その間に CPU が急上昇し、他のすべての人の速度が低下するため、容認できないものです。
もちろん、インデックスの任意の組み合わせを作成できますが、mysql は主に次の組み合わせを使用するのが好きなようです
poster_id + post_time
したがって、100 万件の投稿の中から 1 人のユーザーの 5 万件の投稿を選択し、topic_id によるグループ化と並べ替えを開始します。奇妙なことに、インデックス ミックスに topic_id を追加しても、パフォーマンスは向上しないようですが、インデックス フィールドの順序である可能性があります。
代わりに同等の JOIN を作成して、複数のインデックスを使用できるようにしようとしましたが、post_status と poster によって各側をフィルター処理する必要があるという問題に遭遇しました。
少なくとも最初の数ページについては、mysql が最初に post_time のインデックスを介してデータを並べ替えてから、ユーザーの個別の topic_id を降順で選択し始めることができれば、より高速になると考えていました。それにはサブクエリが必要だと思いますが、50kの結果サブクエリがより良いかどうかはわかりませんが、それでも一時テーブルが必要です。
もちろん、基本的な解決策は、各トピックの各ユーザーの最大 post_time を格納する別のテーブルが存在するようにコア設計を拡張することですが、他の解決策が見つからない場合を除き、これはあまりにも大きな変更です。
ご提案ありがとうございます。
実際の例と EXPLAIN を追加:
遅いログ
# Query_time: 2.751334 Lock_time: 0.000056 Rows_sent: 40 Rows_examined: 48286
SELECT p.*, MAX(post_time) as post_time FROM forum_posts AS p WHERE p.poster_id = '2' AND p.post_status = '0' GROUP BY p.topic_id ORDER BY post_time DESC LIMIT 7000, 40;
説明
select_type table type possible_keys key key_len ref rows Extra
SIMPLE p ref poster_time poster_time 4 const 27072 Using where; Using temporary; Using filesort