MySQL:优化COUNT(*)和GROUP BY

时间:2011-07-13 13:37:32

标签: mysql query-optimization

我有一个类似于以下内容的简单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的事件。查询的答案可能是“今天,事件X对于源A发生了120次,对于源B发生了105次,对于源C发生了900次。”

我编造的查询确实执行了这项任务,但它执行得非常糟糕,在时间跨度设置为“所有时间”时执行时间超过一分钟,并且在一周之内超过30秒:< / p>

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

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

有什么建议吗?

3 个答案:

答案 0 :(得分:0)

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

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

对于event,MySQL可以执行UNIQUE SCAN,这非常快,而对于time,将应用RANGE SCAN,这不是那么快..如果你将索引分开,我希望性能比多列索引更好。

此外,也许你可以通过将你的表分区一些预期的值/值范围来获得一些东西:

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

选择count(source ='A'或NULL)为A,count(source ='B'或NULL)为历史记录中的B; 对于订购,您可以在您的应用程序代码中执行此操作。同时尝试索引事件和源代码。

这肯定比旧版本更快。