优化此MySQL查询的最佳方法是什么?

时间:2009-07-27 23:00:40

标签: python mysql django performance

这是一个总结游戏中每个玩家游戏结果的查询,并显示符合条件的玩家。

select   *, 
         (kills / deaths)  as killdeathratio, 
         (totgames - wins) as losses 
from     (select   gp.name              as name, 
                   gp.gameid            as gameid, 
                   gp.colour            as colour, 
                   Avg(dp.courierkills) as courierkills, 
                   Avg(dp.raxkills)     as raxkills, 
                   Avg(dp.towerkills)   as towerkills, 
                   Avg(dp.assists)      as assists, 
                   Avg(dp.creepdenies)  as creepdenies, 
                   Avg(dp.creepkills)   as creepkills, 
                   Avg(dp.neutralkills) as neutralkills, 
                   Avg(dp.deaths)       as deaths, 
                   Avg(dp.kills)        as kills, 
                   sc.score             as totalscore, 
                   Count(* )            as totgames, 
                   Sum(case 
                         when ((dg.winner = 1 and dp.newcolour < 6) or 
                               (dg.winner = 2 and dp.newcolour > 6)) 
                         then 1 
                         else 0 
                       end) as wins 
          from     gameplayers as gp, 
                   dotagames   as dg, 
                   games       as ga, 
                   dotaplayers as dp, 
                   scores      as sc 
          where    dg.winner <> 0 
                   and dp.gameid = gp.gameid 
                   and dg.gameid = dp.gameid 
                   and dp.gameid = ga.id 
                   and gp.gameid = dg.gameid 
                   and gp.colour = dp.colour 
                   and sc.name   = gp.name 
          group by gp.name 
          having   totgames >= 30
          ) as h 
order by totalscore desc

现在我不太确定最好的方法是什么,但您认为优化此查询会是什么?

我运行了Q6600 @ 2.4ghz,4GB内存,64位Linux Ubuntu 9.04系统,这个查询最多可能需要6.7秒才能运行(我确实有一个巨大的数据库)。

此外,我也希望对结果进行分页,并且在此查询之上执行额外条件的速度太慢了....

我使用django作为前端,所以任何包含使用python +/- django方法的方法都会很棒。 MySQL,Apache2调整也很受欢迎。当然,我愿意更改查询以使其运行更快。

感谢您阅读我的问题;期待阅读你的答案!

编辑:EXPLAIN QUERY RESULTS

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra 
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    783     Using filesort
2   DERIVED     sc  ALL     name,name_2     NULL    NULL    NULL    2099    Using temporary; Using filesort
2   DERIVED     gp  ref     gameid,colour,name  name    17  development.sc.name     2    
2   DERIVED     ga  eq_ref  PRIMARY,id,id_2     PRIMARY     4   development.gp.gameid   1   Using index
2   DERIVED     dg  ref     gameid,winner   gameid  4   development.ga.id   1   Using where
2   DERIVED     dp  ref     gameid_2,colour     gameid_2    4   development.ga.id   10  Using where

4 个答案:

答案 0 :(得分:2)

首先,SQL格式错误。最明显的错误是每个AS子句之前的行拆分。第二个明显的问题是使用隐式连接而不是显式使用INNER JOIN ... ON ...

现在回答实际问题。

在不知道数据或环境的情况下,我首先要看的是一些MySQL服务器设置,例如sort_bufferkey_buffer。如果您没有更改任何这些,请继续阅读它们。默认设置非常保守,通常可以提升超过默认值的十倍,特别是在你的大铁上。

经过审核后,我会运行查询部分以查看速度和EXPLAIN所说的内容。索引的效果可能是深刻的,但MySQL有一个“手指和脚趾”的问题,它不能使用每个表多于一个。带过滤的JOIN可能需要两个。所以它必须下降到行扫描以进行另一次检查。但话说回来,切断查询并尝试不同的组合会告诉你它开始绊脚的地方。

现在您将了解“引爆点”的位置:这是一些原始数据大小的小幅增加,例如需要提取多少,将导致性能大幅下降,因为某些内部结构变得太大了。此时,您可能希望提高临时表的大小。请注意,这种优化是一种黑色艺术。 : - )

然而,还有另一种方法:非规范化。在一个简单的实现中,定期调度的脚本将不时地运行这个昂贵的查询,并将数据戳入一个更接近您想要显示的结构的单独表中。这种方法有多种变体。可以在应用程序中或使用表触发器即时保持最新。在另一个极端,您可以允许您的应用程序偶尔运行昂贵的查询,但缓存结果一段时间。如果很多人会经常调用它,这是最有效的:即使在每秒运行15次的请求上缓存2秒也会显示出明显的改进。

您可以通过运行半打查询来找到生成相同数据的方法,每个查询都会返回一些数据,并对数据进行后处理。您还可以运行原始查询的版本以返回更多数据(这可能会更快,因为它可以减少过滤)并对其进行后处理。我已经多次发现五个更简单,更小的查询可以更快 - 一个数量级,有时两个 - 比一个试图完成所有操作的大查询。

答案 1 :(得分:0)

由于您扫描整个表格,因此没有索引可以帮助您。 随着数据库的增长,查询总是会变慢。

考虑累积统计数据:在每场比赛之后,插入该游戏的行,并在玩家的行中增加计数器,然后你不需要count()和sum()因为信息可用。

答案 2 :(得分:0)

  • 选择*很多次都很糟糕 - 只选择您需要的列
  • 将select分成多个简单选择,在需要时使用临时表
  • 总和(案件部分可以使用子选择
  • 完成
  • mysql与or-expressions的表现非常糟糕。使用两个你联合起来的选择

答案 3 :(得分:0)

小改进

select *, (kills / deaths) as killdeathratio, (totgames - wins) as losses from (select gp.name as name, gp.gameid as gameid, gp.colour as colour, Avg(dp.courierkills) as courierkills, Avg(dp.raxkills) as raxkills, Avg(dp.towerkills) as towerkills, Avg(dp.assists) as assists, Avg(dp.creepdenies) as creepdenies, Avg(dp.creepkills) as creepkills, Avg(dp.neutralkills) as neutralkills, Avg(dp.deaths) as deaths, Avg(dp.kills) as kills, sc.score as totalscore, Count(1 ) as totgames, Sum(case when ((dg.winner = 1 and dp.newcolour < 6) or (dg.winner = 2 and dp.newcolour > 6)) then 1 else 0 end) as wins from gameplayers as gp, ( select * from dotagames dg1 where dg.winner <> 0 ) as dg, games as ga, dotaplayers as dp, scores as sc where and dp.gameid = gp.gameid and dg.gameid = dp.gameid and dp.gameid = ga.id and gp.gameid = dg.gameid and gp.colour = dp.colour and sc.name = gp.name group by gp.name having totgames >= 30 ) as h order by totalscore desc

<强>的变化: 1. count(*)chnaged to count(1) 2.在FROM中,行数减少。