`

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(*) FROM sbtest1; 
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------------+
 | id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows    | Extra       | 
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------------+
 |  1 | SIMPLE      | sbtest1 | index | NULL          | PRIMARY | 4       | NULL | 1000099 | Using index | 
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------------+ 
SELECT COUNT(*) FROM sbtest1;
 +----------+
 | COUNT(*) |
 +----------+ 
|  1000000 |
 +----------+ 
1 row in set (1.42 sec) 

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

explain SELECT COUNT(*) FROM sbtest1 WHERE aid>=0; 
+----+-------------+---------+-------+---------------+---------+---------+------+--------+--------------------------+
 | id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
 +----+-------------+---------+-------+---------------+---------+---------+------+--------+--------------------------+
 |  1 | SIMPLE      | sbtest1 | range | PRIMARY       | PRIMARY | 4       | NULL | 316200 | Using where; Using index |
 +----+-------------+---------+-------+---------------+---------+---------+------+--------+--------------------------+ 
1 row in set (0.00 sec) 
SELECT COUNT(*) FROM sbtest1 WHERE aid>=0;
 +----------+
 | COUNT(*) | 
+----------+ 
|  1000000 |
 +----------+
 1 row in set (1.42 sec) 

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

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

可以看到,采用这种方式查询会非常快。
上面的所有测试,均在 mysql 5.1.24 环境下通过,并且每次查询前都重启了 mysqld。
可以看到,把 aid 和 id 的长度调换之后,采用 secondary index 查询仍然是要比用 primary key 查询来的快很多。看来主要不是字段长度引起的索引扫描快慢,而是采用 primary key 以及 secondary index 引起的区别。那么,为什么用 secondary index 扫描反而比 primary key 扫描来的要快呢?我们就需要了解innodb的 clustered index 和 secondary index 之间的区别了。
innodb 的 clustered index 是把 primary key 以及 row data 保存在一起的,而 secondary index 则是单独存放,然后有个指针指向 primary key。因此,需要进行 count(*) 统计表记录总数时,利用 secondary index 扫描起来,显然更快。而primary key则主要在扫描索引,同时要返回结果记录时的作用较大,例如:

SELECT * FROM sbtest WHERE aid = xxx; 

那既然是使用 secondary index 会比 primary key 更快,为何优化器却优先选择 primary key 来扫描呢,

Heikki Tuuri 的回答是:

in the example table, the secondary index is inserted into in a perfect order! That is very unusual. 
Normally the secondary index would be fragmented, causing random disk I/O, 
and the scan would be slower than in the primary index. I am changing this to a feature request:
 keep 'clustering ratio' statistics on a secondary index and do the scan there if the order is almost
 the same as in the primary index. I doubt this feature will ever be implemented, though. 

 

分享到:
评论

相关推荐

    MySQL Innodb锁解决并发问题

    ### MySQL Innodb锁解决并发问题 #### 一、问题描述及解决过程 在现代数据库管理系统中,处理并发访问是至关重要的。特别是在高并发场景下,如何确保数据的一致性和完整性成为了一个挑战。本文将通过一个具体的...

    关于InnoDB的索引大小

    本篇文章将深入探讨InnoDB存储引擎中的索引大小问题,包括其影响因素、限制以及优化策略。 首先,我们来了解InnoDB索引的基本结构。InnoDB主要使用B+树(B-tree)作为索引的数据结构,无论是主键索引还是辅助索引。...

    MyISAM InnoDB 区别

     6、如果和MyISAM比insert写操作的话,Innodb还达不到MyISAM的写性能,如果是针对基于索引的update操作,虽然MyISAM可能会逊色Innodb,但是那么高并发的写,从库能否追的上也是一个问题,还不如通过多实例分库分表...

    MYSQL innodb性能优化学习总结

    - **innodb_file_per_table**:该参数控制InnoDB是否为每个表使用独立的数据表空间,建议设置为1,以避免共享表空间(ibdata1)可能带来的问题,例如难以进行数据库的备份和恢复。 - **innodb_stats_on_metadata**...

    MySQL InnoDB 查询优化实现分析

    - **附录四**:列举了一些实际应用中的案例研究,展示了如何利用 InnoDB 的特性来解决具体的性能问题。 通过本文的详细解析,读者可以对 MySQL + InnoDB 存储引擎在查询优化方面的工作原理有更深入的理解,并能够在...

    MySQL核心Innodb存储引擎浅析—事务系统

    - 自适应哈希索引(Adaptive Hash Index),通过统计分析自动生成最常使用的索引来加速查询。 - 插入缓冲区(Insert Buffer),优化插入性能。 - 双写缓冲区(Double Write Buffer),增强数据安全性。 #### ...

    关于mysql innodb count(*)速度慢的解决办法

    为了解决InnoDB引擎执行`COUNT(*)`查询速度慢的问题,可以尝试以下几种策略: 1. **利用索引**: 当需要统计特定条件下的行数时,可以创建一个非主键的索引,并在`WHERE`子句中加入条件,例如: ```sql SELECT ...

    关于mysql中innodb的count优化问题分享

    对于`COUNT(*)`,如果直接使用主键进行全表扫描,InnoDB需要遍历整个主键索引,统计所有行,这包括非主键字段的数据。 辅助索引(Secondary Index),也称为非聚集索引,它们不包含数据行的所有信息,仅包含索引...

    myisam innodb对比1

    - 缺点:删除大量数据后可能留下大量空闲空间,不适合需要频繁统计或清理的场景。 - **多表空间存储(独占表空间存储)**:每个表拥有自己独立的表空间文件,通常文件名为表名加上 `.ibd` 扩展名。 - 优点:单个...

    计算机软件-编程源码-InnoDB 中文参考手册.zip

    通过查询INFORMATION_SCHEMA或SHOW STATUS命令,可以获取InnoDB的相关统计信息,帮助诊断和优化性能。例如,检查锁等待情况、内存使用情况、IO性能等。 总的来说,《InnoDB 中文参考手册》涵盖了InnoDB的各个方面,...

    MYSQL-innodb性能优化学习总结[收集].pdf

    8. **innodb_file_per_table**:如果设置为1,每个InnoDB表将拥有独立的数据表空间,有利于管理和备份,但插入操作可能稍慢。默认情况下,所有表共享一个表空间(ibdata1)。 9. **innodb_stats_on_metadata**:...

    为什么 select count(*) from t,在 InnoDB 引擎中比 MyISAM 慢?

    总结来说,`select count(*) from t`在InnoDB中比MyISAM慢主要是因为InnoDB的事务特性和MVCC机制导致的全表扫描。然而,InnoDB通过优化索引遍历来尽可能地提高效率。在实际应用中,为了提高查询速度,可以考虑创建...

    Innodb与Myisam引擎的区别与应用场景

    - **MyISAM**:不支持事务处理,这意味着在进行一系列操作时,如果其中一个操作失败,则不会回滚之前的操作,可能造成数据的一致性问题。 - **InnoDB**:支持事务处理,具备ACID特性(原子性、一致性、隔离性和...

    提供MySQL负载类型、关键统计、慢查询报告等健康信息-Perl-Shell-下载

    本文将深入探讨如何使用Perl和Shell脚本来获取MySQL的负载类型、关键统计信息以及慢查询报告,帮助你更好地理解和优化你的数据库性能。 一、MySQL负载类型 MySQL的负载类型通常包括查询负载、连接负载和I/O负载。...

    innodb_index_stats导入备份数据时报错表主键冲突的解决方法

    标题中提到的“innodb_index_stats导入备份数据时报错表主键冲突”的问题,实质上是数据库操作过程中的一种常见错误。本文将对此问题进行详细解读,包括错误的描述、可能的原因分析以及解决方法。 首先,我们来理解...

    MySQL优化之InnoDB优化

    最后,监控和分析也是优化的重要步骤,使用`SHOW STATUS`和`SHOW ENGINE INNODB STATUS`命令可以查看InnoDB的运行状态,找出潜在的问题。定期分析`EXPLAIN`结果,检查索引使用情况,以及使用慢查询日志来定位慢查询...

    MySQL优化深度分析及问题集锦课堂笔记

    统计计数问题涉及到表的行数计算,join的优化要求合理选择连接类型和连接顺序,以减少不必要的数据扫描。 锁及常见性能问题分析: MySQL中的锁主要分为共享锁和排他锁。锁的性能问题会导致数据库操作变慢,特别是在...

    29.如何判断一个数据库是不是出问题了?1

    综上所述,判断数据库是否出问题需要综合考虑多种因素,包括但不限于监控I/O统计、读写操作、并发线程限制、锁等待情况以及并发查询的处理。通过深入理解这些参数和机制,可以更准确地识别和解决问题,确保数据库...

Global site tag (gtag.js) - Google Analytics