为什么 select count(*) from t,在 InnoDB 引擎中比 MyISAM 慢?
不一定。分两种情况。
1.如果在查询语句中带有where 条件的话。两种表的操作是一样的。
2.如果查询SQL语句不带有where 条件的话。MyISAM 比InnoDB 快。因为在MyISAM 引擎中把一张表的总行数存储在了磁盘上,查询的时候直接返回就行了。而InnoDB 需要全表扫描,计算总行数,所以慢。
为什么 InnoDB不能和MyISAM一样呢?因为事务特性决定,多版本并发控制(MVCC)存在,导致不同时刻看到的结果是不一样的。
count(*) 的具体实现是由存储引擎实现的,也就是说不同的存储引擎实现的方式不一样。
InnoDB和MyISAM 是我们常用的 MySQL 存储引擎,所以主要对比一下 count(*) 在 InnoDB 和 MyISAM 中的实现:
-
统计SQL不带where 条件时:
在MyISAM 存储引擎中,把表的总行数存储在磁盘上,当执行 select cout() from t 时,直接返回总数据的数量。
在InnoDB 存储引擎中,并没有存储总行数在磁盘上,当执行 select cout() from t 时,会先把数据读出来,一行一行的累加。最后返回总数量。 -
统计SQL带where 条件时:
都是一行一行累加。最后返回数量。(ps:MyISAM 中存储的表的总数量)
InnoDB 中count(*) 语句是在执行的时候,全表扫描统计总数量。当数据量越来越大时,语句就会很慢。InnoDB为什么不记录总行数到磁盘上?这和InnoDB的事务特性有关,由于多版本并发控制(MVCC)原因,InnoDB表在查询的时候“应该返回多少行”是不确定的。
不妨用一个例子来说明一下,假设现在 t 表中有 10000 条数据,现在有三个用户同时访问的会话:
- 会话 A 先启动事务并查询一次表的总行数。
- 会话 B 启动事务,插入一行后记录后,查询表的总行数。
- 会话 C 先启动一个单独的语句,插入一行记录后,查询表的总行数。
假如从上到下是按照时间顺序执行的,同一行语句是在同一时间执行的。可以看出在最后时刻。三个回话返回的总行数不一样。
出现不一样的结果跟InnoDB存储引擎有关。在默认隔离级别 可重复读的情况下,通过多版本并发控制(MVCC)来实现,每一行记录都需要判断自己是否对这个会话可见,因此在统计总数量时,InnoDB引擎只有把数据一行一行的读取出来判断,只有当前会话可见才被统计进去。所以同一时刻不同会话查询到的数量就不一样。