告诉你为什么为什么 SELECT COUNT(*) FROM table 在 InnoDB 引擎中比 MyISAM引擎中的速度慢

文章探讨了MySQL中InnoDB和MyISAM存储引擎在执行count(*)查询时的性能差异。InnoDB由于其事务处理和MVCC机制,在没有过滤条件的情况下,count(*)操作需要全表扫描,导致速度较MyISAM慢,后者直接存储表的总行数。然而,InnoDB通过选择最小索引树来优化查询,提高效率。在有过滤条件的查询中,两者的性能比较则取决于具体条件。

统计一张表的总数量,是我们开发中常有的业务需求,通常情况下,我们都是使用 select count(*) from table SQL 语句来完成。随着业务数据的增加,你会发现这条语句执行的速度越来越慢,为什么它会变慢呢?

为什么会变慢?想要得到答案就需要知道 MySQL 是如何统计总数量的,先说一个前提吧,count(*) 的具体实现是由存储引擎实现的,也就是说不同的存储引擎实现的方式不一样。标题:为什么select count( * ) from table,在 InnoDB 引擎中比 MyISAM 慢?也是高频面试题。

InnoDB和MyISAM 是我们常用的 MySQL 存储引擎,所以主要对比一下 count(*) 在 InnoDB 和 MyISAM 中的实现:

  • 「在 MyISAM 存储引擎中,把表的总行数存储在磁盘上,当执行 select count(*) from table 时,直接返回总数据」。
  • 「在 InnoDB 存储引擎中,跟 MyISAM 不一样,没有将总行数存储在磁盘上,当执行 select count(*) from table 时,会先把数据读出来,一行一行的累加,最后返回总数量」。

知道了 InnoDB 和 MyISAM 引擎 count(*) 实现之后,为什么select count(*) from table,在 InnoDB 引擎中比 MyISAM 慢?应该有答案了吧,但是这个结论需要有一个前提,就是统计 SQL 不带过滤条件。如果 统计数量 SQL 语句为:select count(*) from table where x = 23,那么在 MyISAM 中就不一定比 InnoDB 快了。

「InnoDB 中 count(*) 语句是在执行的时候,全表扫描统计总数量,所以当数据越来越大时,语句就越来越耗时了」,为什么 InnoDB 引擎不像 MyISAM 引擎一样,将总行数存储到磁盘上?这跟 InnoDB 的事务特性有关,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的。

不妨用一个例子来说明一下,假设现在 t 表中有 10000 条数据,现在有三个用户同时访问的会话:

  • 会话 A 先启动事务并查询一次表的总行数。
  • 会话 B 启动事务,插入一行后记录后,查询表的总行数。
  • 会话 C 先启动一个单独的语句,插入一行记录后,查询表的总行数。

会话执行流程表

会话A会话B会话C
begin;
select count(*) from table
insert into table (插入一行)
begin;
insert into table(插入一行)
select count(*) from table;(返回10000)select count(*) from table;(返回10002)select count(*) from table;(返回10001)

假设从上到下是按照时间顺序执行的,同一行语句是在同一时刻执行的。可以看出在最后时刻,三个会话返回的总行数不一样。

出现不一样的结果跟 InnoDB 存储引擎有关系,「在默认隔离级别可重复读的情况下,通过多版本并发控制(MVCC)来实现,每一行记录都需要判断自己是否对这个会话可见,因此在统计总数量时,InnoDB 只好把数据一行一行的读取出来判断,只有当前会话可见的才纳入统计中」。所以同一时刻不同会话查询到的数量就不一样。

InnoDB 引擎在 count(*)语句上也做了优化,我们知道,在 InnoDB 存储引擎中是以索引组织表的方式存储数据,主键索引树上叶子节点存放在所有的数据,而普通索引树的叶子节点是主键值,所以普通索引树会比主键索引树小很多,但是数量是一样的,也就是说遍历主键索引树和普通索引树得到的结果都是一样的。MySQL 就利用了这一特性,在 InnoDB 中执行 select count(*) from table 语句时,MySQL 优化器会找到最小的那棵索引树来遍历,这样可能就可以减少加载次数,在一定程度上提升了 count(*)的执行效率。

<think>嗯,用户现在问的是MySQL Explain中type类型为system的含义,以及为什么InnoDB存储引擎中不出现这种类型。我需要结合之前提供的引用内容来回答这个问题。 首先,根据引用[1],system是const的特例,当查询的表只有一行时会使用system。而const类型是当MySQL将查询转换为常量时出现的,比如主键查询。那system只在表里只有一行数据的时候出现,这应该是在MyISAM或者MEMORY这类存储引擎下才会有的情况。 然后,用户问为什么InnoDB不出现system类型。可能的原因是什么呢?InnoDB是支持事务的存储引擎,通常用于需要高并发和事务安全的场景。InnoDB的行数统计可能不像MyISAM那样直接,因为MyISAM会保存表的行数,而InnoDB则需要动态统计,尤其是在有事务隔离的情况下,不同事务可能看到不同的行数。因此,即使表中只有一行数据,InnoDB可能也无法确定这个状态,导致无法触发system类型。 另外,引用[4]提到type类型的顺序是system>const>eq_ref>ref>range>index>ALL,说明system是最快的类型。但InnoDB的设计可能更倾向于使用const而不是system,因为InnoDB的行数统计机制不同,无法保证单行的情况下准确识别,或者因为事务的存在,无法在查询时确定表的行数为1。 还有,需要考虑InnoDB的事务隔离级别。比如,在可重复读隔离级别下,一个事务可能看不到其他事务提交的数据,因此即使表中实际只有一行,事务中的查询可能无法确定这一点,从而无法使用system类型。而MyISAM没有事务支持,行数统计更直接,可以快速确定是否为单行。 总结一下,system类型要求表只有一行数据,而InnoDB由于事务和多版本控制的原因,无法保证在查询时准确判断表的行数是否为1,因此即使表中只有一行,也可能不会显示为system类型,而是使用const。需要结合存储引擎的特性、事务隔离级别和行数统计机制来解释。</think>### MySQL Explain中type=system的含义及InnoDB不出现的原因分析 --- #### **一、type=system的定义与核心特征** 1. **官方定义** `system`是`const`类型的特例,仅当**表中只有一条数据**时触发,属于EXPLAIN输出中最高效的访问类型[^1][^4]。 - **触发条件**: - 表数据行数严格为1 - 查询条件能够通过主键或唯一索引直接定位到该行 - **示例场景**: ```sql -- 当表t1仅包含1行数据时 EXPLAIN SELECT * FROM t1 WHERE primary_key = 1; ``` 输出结果中的`type`列会显示`system`[^1]。 2. **与const类型的区别** | **类型** | **触发条件** | **数据量要求** | |----------|----------------------------|----------------| | `const` | 通过主键/唯一索引定位单行 | 表可含多行数据 | | `system` | 同`const`且表仅1行数据 | 表严格仅1行 | --- #### **二、InnoDB引擎中不出现system类型的根本原因** 1. **数据统计机制差异** - **MyISAM的静态行数统计**: MyISAM引擎直接存储表的总行数(通过`STATS`元数据),能快速判断表是否为单行数据[^1]。 - **InnoDB的动态行数统计**: InnoDB使用多版本并发控制(MVCC),行数是**基于当前事务隔离级别动态计算**的,无法保证全局一致性。即使表中实际只有1行数据,不同事务可能看到不同的行数(例如未提交的事务可能影响可见性)[^3]。 2. **事务与隔离级别的干扰** - 在可重复读(REPEATABLE READ)隔离级别下,InnoDB通过快照读(Snapshot Read)保证事务内数据一致性。即使表中实际仅有1行,其他事务的写入操作可能使得当前事务无法确定表的真实行数。 - **示例**: ```sql -- 事务A START TRANSACTION; SELECT COUNT(*) FROM t1; -- 假设返回1行 -- 事务B(同时插入新数据) INSERT INTO t1 VALUES (2, 'data'); -- 事务A再次查询仍可能看到1行(快照隔离) SELECT * FROM t1 WHERE id = 1; -- EXPLAIN type可能显示const而非system ``` 3. **存储引擎设计目标差异** - **MyISAM适用场景**: 适合只读或读多写少的场景,静态行数统计特性使其更容易触发`system`类型。 - **InnoDB适用场景**: 面向事务安全与高并发,动态行数统计与MVCC机制导致`system`类型几乎不可见,即使表中实际仅有1行,通常仍会显示为`const`[^4]。 --- #### **三、验证实验与现象对比** 1. **MyISAM引擎测试** ```sql CREATE TABLE t_myisam (id INT PRIMARY KEY) ENGINE=MyISAM; INSERT INTO t_myisam VALUES (1); EXPLAIN SELECT * FROM t_myisam WHERE id = 1; -- type: system ``` 2. **InnoDB引擎测试** ```sql CREATE TABLE t_innodb (id INT PRIMARY KEY) ENGINE=InnoDB; INSERT INTO t_innodb VALUES (1); EXPLAIN SELECT * FROM t_innodb WHERE id = 1; -- type: const(非system) ``` --- #### **四、总结与调优建议** 1. **核心结论** - `system`类型仅在**非事务型存储引擎**(如MyISAM)且表严格单行时出现。 - InnoDB因MVCC和动态行数统计机制,**无法稳定满足单行确定性**,故统一使用`const`类型[^1][^4]。 2. **调优方向** - 避免依赖`system`类型优化查询,应聚焦于`const`/`eq_ref`等通用高效访问类型。 - 对于单行高频查询的表,可通过缓存(如Redis)降低数据库压力。 --- ### 相关问题 1. **如何在InnoDB中模拟`system`类型的查询效果?** (需强制关闭事务隔离特性,但会破坏ACID保证)[^3] 2. **MyISAMInnoDB在EXPLAIN结果中的其他差异有哪些?** (对比`rows`统计精度、`index`类型使用频率等)[^4] 3. **事务隔离级别如何影响EXPLAIN的输出结果?** (分析READ COMMITTED与REPEATABLE READ下的差异)[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖天才小朱(怀玉)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值