日期:2014-05-16  浏览次数:20530 次

innodb统计慢的问题

?

(转载)innodb count(*) ?

(原文地址:http://imysql.cn/2008_06_24_speedup_innodb_count)

起因:在innodb表上做count(*)统计实在是太慢了,因此想办法看能不能再快点。
现象:先来看几个测试案例,如下
一、 sbtest 表上的测试

show create table sbtest\G 
*************************** 1. row ***************************
?Table: sbtest Create Table:
?CREATE TABLE `sbtest` ( `aid` bigint(20) unsigned NOT NULL auto_increment, 
`id` int(10) unsigned NOT NULL default '0', `k` int(10) unsigned NOT NULL default '0',
?`c` char(120) NOT NULL default '', `pad` char(60) NOT NULL default '', 
PRIMARY KEY  (`aid`), KEY `k` (`k`), KEY `id` (`id`) ) 
ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1 填充了 1000万条 记录。

1、 直接 count(*)

explain SELECT COUNT(*) FROM sbtest; 
+----+-------------+--------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
?+----+-------------+--------+-------+---------------+---------+---------+------+---------+-------------+ 
|  1 | SIMPLE      | sbtest | index | NULL          | PRIMARY | 8       | NULL | 1000099 | Using index | 
+----+-------------+--------+-------+---------------+---------+---------+------+---------+-------------+
?SELECT COUNT(*) FROM sbtest;
?+----------+
?| COUNT(*) | 
+----------+
?|  1000000 | 
+----------+
?1 row in set (1.42 sec) 

可以看到,如果不加任何条件,那么优化器优先采用 primary key 来进行扫描。
2、count(*) 使用 primary key 字段做条件

explain SELECT COUNT(*) FROM sbtest WHERE aid>=0; 
+----+-------------+--------+-------+---------------+---------+---------+------+--------+--------------------------+
?| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    | 
+----+-------------+--------+-------+---------------+---------+---------+------+--------+--------------------------+
?|  1 | SIMPLE      | sbtest | range | PRIMARY       | PRIMARY | 8       | NULL | 485600 | Using where; Using index | 
+----+-------------+--------+-------+---------------+---------+---------+------+--------+--------------------------+ 
SELECT COUNT(*) FROM sbtest WHERE aid>=0; 
+----------+
?| COUNT(*) | 
+----------+
?|  1000000 |
?+----------+
?1 row in set (1.39 sec) 

可以看到,尽管优化器认为只需要扫描 485600 条记录(其实是索引),比刚才少多了,但其实仍然要做全表(索引)扫描。因此耗时和第一种相当。
3、 count(*) 使用 secondary index 字段做条件

explain SELECT COUNT(*) FROM sbtest WHERE id>=0;
?+----+-------------+--------+-------+---------------+------+---------+------+--------+--------------------------+
?| id | select_type | table  | type  | possible_keys | key  | key_len | ref  | rows   | Extra                    | 
+----+-------------+--------+-------+---------------+------+---------+------+--------+--------------------------+
?|  1 | SIMPLE      | sbtest | range | id            | id   | 4       | NULL | 500049 | Using where; Using index | 
+----+-------------+--------+-------+---------------+------+---------+------+--------+--------------------------+ 
SELECT COUNT(*) FROM sbtest WHERE id>=0; 
+----------+ 
| COUNT(*) | 
+----------+
?|  1000000 | 
+----------+
?1 row in set (0.43 sec) 

可以看到,采用这种方式查询会非常快。
有人也许会问了,会不会是因为 id 字段的长度比 aid 字段的长度来的小,导致它扫描起来比较快呢?先不着急下结论,咱们来看看下面的测试例子。
二、 sbtest1 表上的测试

show create table sbtest1\G
?*************************** 1. row ***************************
?Table: sbtest1 Create Table: CREATE TABLE `sbtest1` ( `aid` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`id` bigint(20) unsigned NOT NULL DEFAULT '0', `k` int(10) unsigned NOT NULL DEFAULT '0',
?`c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', 
PRIMARY KEY (`aid`), KEY `k` (`k`), KEY `id` (`id`) ) 
ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1这个表里,把 aid 和 id 的字段长度调换了一下,也填充了 1000万条 记录。

1、 直接 count(*)

explain SELECT COUNT(*)