2012-01-17 93 views
1

我正在研究一個與應用有關的體育賽事的「排行榜」,該體育賽事根據他們的分數報告前20位用戶所有他們對多項選擇測驗的迴應。它還在排行榜中顯示當前用戶自己的排名。MySQL:緩慢查詢使用「group by」 - 卡在「複製到tmp表」

當此應用程序正在進行負載測試時,有關的兩個查詢變得非常慢,在「複製到tmp表」狀態(每個查詢最多20秒)中花費了大量時間。他們最終做了處理,但同時數百人可以堆積起來。

在隔離給出合理的數目在響應表中的行,每個查詢需要約1秒至執行(25K用戶,例如,在響應200K行)

我已經添加一些索引有關的表,特別是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表」需要很長時間

約束:未顯示,用戶具有teamID,查詢也通過teamID進行過濾。也沒有顯示,有幾個事件,這些查詢也可以通過eventID過濾。此外,並非所有問題在回答時都有正確的答案。在未來某個時候可能會分配正確的答案,但無論如何在體育賽事結束時。系統報告用戶選擇每個答案的百分比。因此,以更加合計的方式存儲分數的各種方式已被考慮但被丟棄,因爲它們與一個或多個這些限制相沖突。

希望這足以去 - 許多感謝

回答

2

我做這樣的東西,並有類似的問題。同時查詢堆積起來是因爲它們需要序列化,因此它們每個都在運行時返回正確的結果。

非常適合您在負載測試中使用,而不是在生產中使用。

你如何解決這個問題?

  1. 創建與摘要查詢結果具有相同列的摘要表。
  2. 創建存儲過程以從您的詳細表中提取彙總數據並重寫彙總表。
  3. 創建一個事件以適當的時間間隔運行存儲過程。您的排行榜顯示器有多陳舊?六秒鐘,一分鐘一小時?這就是您的活動應該運行的頻率。您的問題不是排行榜提取查詢的基本成本。問題來自試圖每分鐘運行數十億次。
  4. 重寫您的排行榜顯示,以將內容從摘要表中提取出來。

這樣你就可以爲每個人做一次困難的事情,而且每個用戶都能輕鬆做到。

這將穩定你的應用程序,並讓它很好地擴展。

+0

感謝這個;感謝您的回覆。我認爲實現了一些模糊的類似的東西,但是在彙總表重新填充時,如果排行榜請求進入,會發生什麼? – Polsonby 2012-01-17 15:40:25

+0

你在使用InnoDB嗎?如果是這樣,您的更新查詢將鎖定您的彙總表,並且用戶請求將在第二秒鐘或之後掛起以生成它,然後正常完成。如果你正在使用MyISAM,你的存儲過程應該可能顯式地鎖定彙總表以獲得相同的效果。如果所有這些都出現了一個可怕的問題,您可以嘗試創建一個新表格,然後鎖定舊錶格,刪除它,並將新表格重新命名爲舊名稱。但這是一個全面的毛球來調試。 – 2012-01-18 02:41:57