Mysql 分组查询优化
数据库是MySQL和MyISAM引擎 表定义:Mysql 分组查询优化,mysql,group-by,query-optimization,myisam,Mysql,Group By,Query Optimization,Myisam,数据库是MySQL和MyISAM引擎 表定义: 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,
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 ;
我已在(游戏、完成、用户)
上设置了索引,但此分组依据
查询仍需要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
解释
输出:
| 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,也不会影响性能。我试过不按下单,但它仍然需要几乎相同的时间。去掉“游戏”键-它与“I_gfu”是多余的。因为“id”是唯一的count(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
嗯,很难。尝试重新排序您的索引:将
用户
列放在第一位(因此将索引设置为(用户、完成、游戏)
),因为这会增加组成员使用索引的机会。但是,在一般情况下,如果将使用的聚合函数限制为MIN和MAX(请参阅和),则GROUP BY只能使用索引。您的order by也没有真正的帮助。解释验证了查询中使用的(游戏、完成、用户)
索引。对我来说,这似乎是最好的索引。这可能是硬件问题吗?您的系统RAM和CPU是什么?我认为大部分时间都花在提取800k行中的150k行上,更重要的是排序(两次,包括通过读取索引跳过的一次)。我怀疑您是否能够对其进行比现在更多的优化。正如其他人所指出的,您可能已经达到了优化查询本身的能力极限。接下来,您应该查看服务器中max\u heap\u table\u size
和tmp\u table\u size
变量的设置。默认值为16MB,这对于您的表来说可能太小。此查询的缺点之一是您按聚合排序。这意味着在生成完整的结果集之前,不能返回任何行;没有索引可以修复这个问题(无论如何,对于mysql myisam)
不过,您可以相当容易地对数据进行反规范化,以克服这一问题;例如,您可以添加一个insert/update触发器,在摘要表中添加一个带有索引的计数值,以便立即开始返回行 是的。排序不,它不花时间排序。这不是你的查询计划所建议的。也不是你的问题。他们都说至少需要一种我的意思是,它花在分类上的时间比花在分组上的时间要短。我也不能怪它这么做。。。它根据您的查询计划将许多行(表的一半?)分组为150k行。:-)事实上,我99%肯定你在浪费时间试图优化它:你当前的三列索引允许直接进入颈静脉,就像在获取相关行并按原样分组一样。然后需要对它们进行分类,这也需要时间。老实说,我认为你们还可以做其他事情。如果有什么不同的话,我真的很惊讶计划者竟然决定使用索引,因为你要检索20%的表。我尝试过该索引,还尝试了
(用户、游戏、完成)
,并强制使用它,但速度更慢。奇怪。我有种感觉,如果将GROUPBY和ORDERBY结合起来,您将无法做得更好:如果查询速度太慢,您可能需要创建一个显式聚合表。使用filesort显示的事实表明,无法从任何索引中执行排序:可能尝试将id
添加到索引中?你是说(游戏,完成,用户,id)
索引?好吧,我会说,在大小上尝试一下,但如果使用COUNT(*)
有帮助,那可能不会有多大好处。内存是1GB。CPU是(我认为)AMD Opteron四核3.5GHz。我猜你的瓶颈是RAM。我建议将其提升到4GB.4GB以处理900k行~30字节的表?;)甚至还不到30兆字节;)@lucek你的数学是正确的,但是现在操作系统开销占用了很多内存。此外,任何其他正在运行的应用程序都将消耗RAM。现在4GB已经是相当标准的了。@也许这里有一个基于软件的建议,可以加快速度。您的表、索引和SQL结构对我来说似乎很好,所以我怀疑任何调整都会有所帮助。@Thomas Jones Low关于服务器变量的建议可能会有所帮助。如果没有什么帮助的话,额外几GB的RAM是相当便宜的。thnx的建议是,两种设置都是64M。count(*)的性能比count(id)快得多的原因是MySQL对count(*)的情况进行了特定的优化。count(id)案例第二次遍历数据以检索结果,其中count(*)使用现有的内部行计数器。尽可能使用count(*)。