2012-01-17 14 views
1

スポーツイベントに関するWebアプリケーションの「リーダーボード」に取り組んでいます。すべての選択肢のクイズへの彼らの応答。また、現在のユーザーのランクをリーダーボード内に表示します。MySQL:「グループ化」を使用した低速クエリ - 「tmpテーブルへのコピー」でスタック

このアプリケーションが負荷テストされると、関連する2つのクエリは非常に遅くなり、 "tmpテーブルへのコピー"状態(クエリごとに最大20秒)で多くの時間を費やします。彼らは最終的に処理を行いますが、その間に何百人も積み重ねられます。単独で

応答テーブル内の行の適切な数を与え、各クエリは、(応答で25Kユーザ、例えば200K行)を実行するのに約1秒かかり

Iは、特に、当該テーブルにいくつかのインデックスを追加しましたFK列とwhere文で使用されるものについては、また、応答テーブルにuserID、answerIDのカバリングインデックスを追加しました。

これは、これは結果内のユーザー自身のランクを取得するためのクエリでリーダーボード自体

SELECT users.username, sum(questions.points) as score FROM responses 
JOIN answers on responses.answerID = answers.answerID 
JOIN questions on answers.questionID = questions.questionID 
JOIN users on responses.userID = users.userID 
WHERE users.username != '' AND answers.isCorrect 
GROUP BY users.userID 
ORDER BY score DESC 
LIMIT 20 

ためのクエリです。別のクエリで最初にスコアを取得した後、スコアの高いユーザーの数をカウントします。

Select count(*) +1 as rank from (
    SELECT users.username, sum(questions.points) as score 
    FROM responses 
    JOIN answers on responses.answerID = answers.answerID 
    JOIN questions on answers.questionID = questions.questionID 
    JOIN users on responses.userID = users.userID 
    WHERE users.username != '' AND answers.isCorrect 
    GROUP BY users.userID 
    HAVING sum(questions.points) > 2431 
    ORDER BY score DESC 
) as result 

簡体スキーマは

QUESTIONS 
questionID 
question 
points 

ANSWERS (multiple choice answers for question) 
answerID 
questionID 
answer 
isCorrect 

RESPONSES (the player's choice of answer) 
responseID 
answerID 
userID 

が、私はこれらのクエリは漠然と賢明な方法で行われていると思うが、私は私が持っているこれらのいずれかを行うには明らかに、より良い方法があるかどうかを知りたいです考慮されていません。

また、これらのクエリが「tmpテーブルへのコピー」状態でスタックし、サーバーの負荷がかかっているときに処理時間がかかる理由について、誰もが考えていますか?私はそれがディスク上にそれらを作成するかもしれないと思ったが、私はそれが別の状態メッセージであると思う。私はEXPLAINを使用しましたが、私の感想は、これらのクエリで一時テーブルが避けられないということです。したがって、「コピーするtmpテーブル」には時間がかかります

制約:示されていません。ユーザーにはチームIDがあり、クエリもteamIDによってフィルタリングされます。また、図示されていないが、いくつかのイベントが存在し、これらのクエリをeventIDによってフィルタリングすることもできる。また、すべての質問が答えられた時点で正しい答えを持っているわけではありません。将来のある時点で正しい答えが割り当てられるかもしれませんが、スポーツイベントの終わりにはどんな速度でも割り当てられます。システムは、各回答を選択したユーザーの割合を報告します。そのため、スコアをより集約して格納するさまざまな方法が検討されていますが、これらの制約の1つまたは複数と競合するため、破棄されています。これは上に行くには十分である

・ホープ - 多くのおかげで

答えて

2

私はこのようなものをやって、同様の問題がありました。同時問合せは、直列化する必要があるため、各問合せが実行された時点で正しい結果を返すため、積み重ねられます。

プロダクションではなく、負荷テストでキャッチするのに適しています。

これをどのように解決しますか?

  1. 要約クエリ結果と同じ列を持つサマリーテーブルを作成します。
  2. ストアドプロシージャを作成して、詳細テーブルからサマリーデータを抽出し、サマリーテーブルを書き換えます。
  3. 適切な間隔でストアドプロシージャを実行するイベントを作成します。あなたのリーダーボードの表示はどれほど古くなりますか? 6秒、1分、1時間?それはあなたのイベントがどのくらいの頻度で実行されるべきかです。あなたの問題は、リーダーボード抽出クエリの基本コストではありません。問題は1分に何百回も実行しようとしたことに由来します。
  4. リーダーボードの表示を書き換えて、サマリーテーブルから情報を引き出します。

このようにして、すべての人に一度やりとりすることができます。

これは、アプリケーションを安定させ、うまくスケールアップさせます。

+0

このをありがとう。あなたの返信を感謝します。私は曖昧に何かを実装することを検討しましたが、サマリーテーブルが再投入されている間にリーダーボードリクエストが来たらどうなりますか? – Polsonby

+0

InnoDBを使用していますか?そうであれば、更新クエリによってサマリテーブルがロックされ、ユーザー要求は2番目にハングアップするか、または生成され、その後正常に完了します。 MyISAMを使用している場合、ストアドプロシージャはサマリーテーブルを明示的にロックして、同じ効果を得られるはずです。このすべてにひどい問題がある場合は、新しいテーブルを作成し、古いテーブルをロックして削除し、古いテーブルの名前に変更することができます。しかし、それはデバッグするための完全なヘアボールです。 –

0

は、句と句BY、また、そこに記載されている他の例では異なるGROUP BY ORDERがある場合、MySQLは一時テーブルを使用していますhttp://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html

を見てみましょう。あなたはそれをバイパスすることはできません。

で説明したようにだからあなたの場合には、このための最も簡単な解決策は、RAMディスクを設定するとMySQLが一時テーブルを格納することできる。

skip copying to tmp table on disk mysql

関連する問題