2012-01-17 89 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