GROUP BY查询优化

时间:2011-05-20 12:26:57

标签: mysql group-by query-optimization myisam

数据库是带有MyISAM引擎的MySQL。

表格定义:

CREATE TABLE IF NOT EXISTS  matches  (
   id  int(11) NOT NULL AUTO_INCREMENT,
   game  int(11) NOT NULL,
   user  int(11) NOT NULL,
   opponent  int(11) NOT NULL,
   tournament  int(11) NOT NULL,
   score  int(11) NOT NULL,
   finish  tinyint(4) NOT NULL,
  PRIMARY KEY ( id ),
  KEY  game  ( game ),
  KEY  user  ( user ),
  KEY  i_gfu ( game , finish , user )
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3149047 ;

我已在(game, finish, user)设置了索引,但此GROUP BY查询仍需要0.4 - 0.6秒才能运行:

SELECT user AS player
     , COUNT( id ) AS times
FROM matches
WHERE finish = 1
  AND game = 19
GROUP BY user
ORDER BY times DESC

EXPLAIN输出:

| id | select_type | table   | type | possible_keys | key   | key_len | 
|  1 |  SIMPLE     | matches |  ref | game,i_gfu    | i_gfu |    5    | 

|  ref        |   rows |   Extra                                      |
| const,const | 155855 | Using where; Using temporary; Using filesort |

有什么方法可以让它更快?该表有大约800K记录。


编辑:我将COUNT(id)更改为COUNT(*),时间降至0.08 - 0.12秒。我想在制作索引之前我已经尝试过了,但是忘了在之后再次改变它。

在解释输出中,使用索引解释了加速:

|   rows |   Extra                                                   |
| 168029 | Using where; Using index; Using temporary; Using filesort |

(旁边的问题:这是正常的5倍下降?)

大约有2000个用户,所以最后排序,即使它使用filesort,也不会影响性能。我试过没有ORDER BY但它仍然需要几乎相同的时间。

6 个答案:

答案 0 :(得分:7)

摆脱'游戏'键 - 它与'i_gfu'是多余的。由于'id'是唯一计数(id),因此只返回每个组中的行数,因此您可以删除它并将其替换为count(*)。尝试这种方式并粘贴EXPLAIN的输出:

SELECT user AS player, COUNT(*) AS times
FROM matches
WHERE finish = 1
AND game = 19
GROUP BY user
ORDER BY times DESC

答案 1 :(得分:2)

呃,很难。尝试重新排序索引:首先放置user列(因此请创建索引(user, finish, game)),因为这会增加GROUP BY可以使用索引的机会。但是,一般情况下,如果限制用于MIN和MAX的聚合函数,GROUP BY只能使用索引(请参阅http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.htmlhttp://dev.mysql.com/doc/refman/5.5/en/loose-index-scan.html)。您的订单也没有真正帮助。

答案 2 :(得分:1)

EXPLAIN验证查询中使用的(game, finish, user)索引。这对我来说似乎是最好的索引。这可能是硬件问题吗?你的系统RAM和CPU是什么?

答案 3 :(得分:1)

我认为大部分时间用于提取,更重要的是排序(两次,包括通过阅读索引跳过的那一次)800k中的150k行。我怀疑你能比现在更加优化它。

答案 4 :(得分:1)

正如其他人所说,您可能已达到调整查询本身的能力极限。您接下来应该看到服务器中max_heap_table_sizetmp_table_size变量的设置。默认值为16MB,对于您的表格而言可能太小。

答案 5 :(得分:1)

此查询的缺点之一是您按聚合顺序排序。这意味着在生成完整的结果集之前,您不能返回任何行;没有索引可以存在(对于mysql myisam,无论如何)来修复它。

尽管如此,您可以非常轻松地对数据进行非规范化处理。例如,您可以添加插入/更新触发器以将计数值粘贴到带有索引的摘要表中,以便您可以立即开始返回行。