当表很大时,mysql查询不使用索引

时间:2012-08-28 12:48:02

标签: mysql indexing

我有一张桌子:

CREATE TABLE `p` (  
`id` bigint(20) unsigned NOT NULL,  
`rtime` datetime NOT NULL,  
`d` int(10) NOT NULL,  
`n` int(10) NOT NULL,  
PRIMARY KEY (`rtime`,`id`,`d`) USING BTREE  
) ENGINE=MyISAM DEFAULT CHARSET=latin1;  

我有一个问题:

select id, d, sum(n) from p where  rtime between '2012-08-25' and date(now()) group by id, d;

我在一张小桌子上运行这个查询的解释(2条记录),它告诉我它将使用我的PK:

id  | select_type  | table | type   | possible_keys key  | key     | key_len | ref  | rows | Extra
1   | SIMPLE       | p     | range  | PRIMARY            | PRIMARY | 8       | NULL | 1    | Using where; Using temporary; Using filesort

但是当我在同一张桌子上使用相同的查询时 - 只有这次它是巨大的(3.5亿条记录) - 它更喜欢浏览所有记录并忽略我的密钥

id  | select_type  | table  | type | possible_keys  | key  | key_len | ref  | rows      | Extra
1   | SIMPLE       | p      | ALL  | PRIMARY        | NULL | NULL    | NULL | 355465280 | Using where; Using temporary; Using filesort
显然,这非常慢.. 有人可以帮忙吗?

编辑:这个简单的查询也花了很多时间:

select count(*) from propagation_delay where  rtime > '2012-08-28';

4 个答案:

答案 0 :(得分:1)

您的查询:

...WHERE rtime between '2012-08-25' and date(now()) group by id, d;

使用rtime,按ID和d分组。至少你应该按rtime索引。您可能还想尝试按此顺序按rtime, id, d, n进行索引,但是当您这样做时,您会看到您的索引将包含与您的表格大致相同的数据。

可能,优化器会进行一些计算,并得出结论:使用索引并不值得。

我只在rtime留下一个索引。真正的关键是有多少记录与WHERE匹配 - 如果它们只是少数记录,那么读取索引并在表格中跳转是很方便的。如果他们是几个,也许最好顺序扫描整个表格,节省往返阅读。

  

这个查询在350万美元中占了很大比重 - 我会说几百万

好吧,那么从索引中快速提取半打记录的累积成本很可能,然后从主表往返来恢复那半打的记录,超过了打开的成本主表,拖曳所有350M记录分组和总结。

在这种情况下, if 你总是(或大部分)在rtime上运行聚合查询,并且该表是一个累积(历史)表,并且每对{{1}每天看到几十个条目,你可以考虑创建一个按日期聚合二级表。即,在(比如)午夜,您运行查询并

(id, d)

INSERT INTO aggregate_table SELECT DATE(@yesterday) AS rtime, id, d, sum(n) AS n FROM main_table WHERE DATE(rtime) = @yesterday GROUP BY id, d; 中的数据只有每一对aggregate_table只有(id, d)当天的一个条目;表格按比例缩小,查询速度更快。这假设您的n数量相对较少,并且每个都会在主表中每天生成大量行。

每对夫妇每分钟记录一次,聚合应该可以将事物加速超过三个数量级(相反,如果你每天两次使用大量不同的传感器,那么效益可以忽略不计)。 / p>

答案 1 :(得分:1)

在你的第二个查询中,日期范围将返回MySQL决定不使用索引的那么多行。这样做是因为n未包含在索引中。非覆盖索引仍然是查找,并且执行大量查找比扫描表更慢。

为了利用索引,您需要减少所选行的数量,或在索引中包含n以获得完整的“覆盖”索引。

答案 2 :(得分:0)

您可能让MySQL使用Index Hint Syntax的某个索引。

答案 3 :(得分:-1)

只是预感,在后面有一点经验,尝试将引擎从MyISAM更改为InnoDB。 MyISAM在许多录音和其他错误方面存在一些问题,InnoDB现在更好了。 此外,从MySQL 5.5开始,默认引擎是InnoDB:http://dev.mysql.com/doc/refman/5.5/en/innodb-default-se.html