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
值的数量。
对于event
,mysql 可以执行一个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优化)