MySQL查询优化技巧

时间:2011-11-08 07:52:56

标签: mysql query-optimization

我有一个游戏应用程序,其中用户用户回答问题,评级基于回答此问题所用的时间。

我正在尝试构建一个返回前20名玩家评级的查询。游戏有一些阶段,我需要检索播放所有阶段的玩家(假设阶段的数量是5)

这就是我写的:

SELECT `usersname` , `time`
FROM `users`
WHERE `users`.`id`
IN (
SELECT `steps`.`user_id`
FROM `steps`
GROUP BY `steps`.`user_id`
HAVING COUNT( `steps`.`id` ) = 5
)
ORDER BY `time` ASC
LIMIT 20

在内部选择中我选择了已经玩过5个阶段(步骤)的所有user_id。查询工作正常,但速度非常慢。执行需要大约一分半钟。你能提供一些优化技巧吗?内部选择返回约2000行。

如果您需要其他信息,请随时问我。

4 个答案:

答案 0 :(得分:2)

假设你有一个关于users.time的索引,这是第一个明显的优化,在内部查询中用WHERE替换HAVING可能值得一试。
如果幸运的话,查询优化器可能已经这样做了,但你不能依赖它,严格按照规范,HAVING在获取每条记录后运行,而WHERE在之前修剪它们

如果这没有帮助,只需为在users表中完成的每个阶段增加一个计数器可能会加快速度,从而消除子查询。这将使完成一个阶段的速度变得极慢(但这不会每秒发生一百万次!),但只能查询已经完成所有5个阶段的用户(特别是如果你有该字段的索引)

此外,使用memcached或类似的缓存技术可能值得像高分一样,这通常是“不一定100%准确到第二,变化缓慢,数十亿次查询”数据。
如果memcached不是一个选项,即使将结果写入临时文件并重新使用它1-2秒(甚至更长)也是一种选择。没有人会注意到。即使您将高分数缓存长达1-2分钟,仍然没有人会冒犯,因为这只是“需要多长时间”。

答案 1 :(得分:2)

尝试使用JOIN,而不是IN (SELECT ...)

SELECT usersname , `time`
FROM users
  JOIN 
    ( SELECT steps.user_id
      FROM steps
      GROUP BY steps.user_id
      HAVING COUNT(*) = 5
    ) grp
    ON grp.user_id = users.id
ORDER BY `time` ASC
LIMIT 20

答案 2 :(得分:0)

我认为你应该使用而不是拥有。此外,在我看来,你应该在存储函数中执行此操作。在我看来,最好的方法是使用where而不是运行内部查询,存储结果并根据内部查询的结果运行外部查询。

答案 3 :(得分:0)

此用例可能会从非规范化中受益。无需搜索所有2000个用户记录来确定用户是否优于20个记录。

  • 创建Top_20_Users表。
  • 在第5阶段之后检查用户的时间是否小于Top_20_Users表中的任何时间。如果是,请更新最慢/最差记录。

你可以做的事情。

  • 由于Top_20_Users表格太小,因此为舞台添加一个字段,并为每个阶段以及所有五个完成阶段包括前20个。

  • 让Top_20_Users表增长。有史以来所有前20名用户的历史,他们的时间和时间足以成为前20名的日期。当用户了解游戏并且前20次变得越来越好时,显示趋势。