我有一个类似于以下内容的简单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
我已尝试过各种多列索引(例如(事件,时间)),但没有任何改进。这似乎是一个常见的用例,我无法想象没有合理的解决方案,但我的谷歌搜索都归结为我已经拥有的查询的版本,没有特别的建议如何避免临时(甚至那时,为什么表现如此糟糕)。
有什么建议吗?
答案 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; 对于订购,您可以在您的应用程序代码中执行此操作。同时尝试索引事件和源代码。
这肯定比旧版本更快。