MySQL:优化 COUNT(*) 和 GROUP BY

Posted

技术标签:

【中文标题】MySQL:优化 COUNT(*) 和 GROUP BY【英文标题】:MySQL: Optimizing COUNT(*) and GROUP BY 【发布时间】:2011-07-13 13:37:32 【问题描述】:

我有一个简单的 MyISAM 表,类似于以下内容(为了便于阅读而进行了修剪——实际上,有更多列,所有列的宽度都是恒定的,其中一些可以为空):

CREATE TABLE IF NOT EXISTS `history` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `time` int(11) NOT NULL,
  `event` int(11) NOT NULL,
  `source` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `event` (`event`),
  KEY `time` (`time`),
);

目前该表仅包含大约 6,000,000 行(其中当前大约 160,000 行与下面的查询匹配),但预计会增加。给定一个特定的事件 ID 并按源分组,我想知道在特定时间间隔内记录了多少具有该 ID 的事件。查询的答案可能类似于“今天,源 A 发生了 120 次事件 X,源 B 发生了 105 次,源 C 发生了 900 次”。

我编写的查询确实执行了这个任务,但是它执行得非常糟糕,当时间跨度设置为“所有时间”时需要一分钟多的时间才能执行,并且在短短一周内超过 30 秒:

SELECT COUNT(*) AS count FROM history
WHERE event=2000 AND time >= 0 AND time < 1310563644
GROUP BY source
ORDER BY count DESC

这不是实时使用的,所以即使查询需要一两秒钟也可以,但几分钟就不行。解释查询给出了以下内容,这让我很困扰,原因很明显:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          history ref     event,time      event   4           const   160399  Using where; Using temporary; Using filesort

我尝试了各种多列索引(例如(事件、时间)),但没有任何改进。这似乎是一个常见的用例,我无法想象没有合理的解决方案,但我的谷歌搜索都归结为我已经拥有的查询版本,没有关于如何避免临时的特别建议(即使这样,为什么性能如此糟糕)。

有什么建议吗?

【问题讨论】:

【参考方案1】:

您说您尝试过多列索引。您是否也尝试过单列索引,每列一个?

更新:另外,COUNT(*)GROUP BY 子句的操作可能要快得多,如果分组列上也有索引...当然,这取决于实际在该列中但未编入索引的 NULL 值的数量。

对于eventmysql 可以执行一个UNIQUE SCAN,这相当快,而对于time,将应用一个RANGE SCAN,这不是那么快......如果你分开索引,我' d 期望比使用多列的性能更好。

另外,也许您可​​以通过按一些预期值/值范围对表进行分区来获得一些好处:

http://dev.mysql.com/doc/refman/5.5/en/partitioning-overview.html

【讨论】:

从顶部的架构中可以看出,除了我尝试的多列索引之外,事件和时间都分别被索引。 对不起,我错过了。我对使用KEY 关键字指定INDEX 的语法不太熟悉...如何将INDEX 添加到source phpmyadmin 的导出功能——我也不习惯。 :) 此外,在源代码上的索引在我的测试中没有提供额外的好处。 你能把 INDEX 添加到 source AND 使它成为 NOT NULL 吗? 如果可以避免,我宁愿尽量避免篡改列定义——如果其他方法都失败了,我会试一试。【参考方案2】:

我建议你试试这个多列索引:

ALTER TABLE `history` ADD INDEX `history_index` (`event` ASC, `time` ASC, `source` ASC);

如果没有帮助,请尝试在此查询上强制索引:

SELECT COUNT(*) AS count FROM history USE INDEX (history_index)
WHERE event=2000 AND time >= 0 AND time < 1310563644
GROUP BY source
ORDER BY count DESC

【讨论】:

那个特定的索引是我在玩多列索引时尝试过的。强制使用索引似乎不会影响性能。【参考方案3】:

如果来源已知或者您想查找特定来​​源的计数,那么您可以这样尝试。

从历史中选择count(source= 'A' or NULL) as A,count(source= 'B' or NULL) as B; 对于订购,您可以在您的应用程序代码中进行。也可以尝试同时索引事件和源。

这肯定会比旧的更快。

【讨论】:

有数百个不同的来源,我需要在同一个查询中为所有这些来源提供数据。 你能指定编号吗? of rows 单独匹配时间条件和单独事件条件。我的意思是从时间>0和时间),从事件= 2000的历史中选择count() 你能添加'show variables like '%table%'的结果吗? ' 并显示类似 '%tmp%' 的状态;在执行刷新状态和您的查询之后。

以上是关于MySQL:优化 COUNT(*) 和 GROUP BY的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 进阶 索引 -- SQL优化(插入数据优化:导入本地文件数据主键优化order by优化group by优化limit优化count优化update优化)

MySQL 原理与优化,Group By 优化 技巧

mysql的group by优化跟limit优化

mysql中group by优化

使用 group by、inner query 和 count 优化查询

mysql中去重 用group by优化distinct 用法