MySQL中MyISAM与InnoDB表COUNT(*)执行差异及脚本咨询
嘿,我来给你掰扯清楚这个问题——你现在用的是MyISAM引擎的表,刚好能直观对比它和InnoDB在COUNT(*)上的核心差异,咱们结合你的表结构来聊:
先看你当前用的MyISAM:COUNT(*)是"走捷径"的
MyISAM有个很特殊的设计:它会在表的元数据文件里专门维护一个精确的总行数,每次你插入、删除、更新数据时,这个数值都会自动同步更新。
所以当你执行SELECT COUNT(*) FROM org_apiinteg_assets;或者SELECT COUNT(*) FROM assessmentinstances;这类不带WHERE条件的统计时,MySQL根本不用去扫表、遍历索引,直接从元数据里把预存的数值读出来就行,速度快到离谱,属于O(1)级别的操作。
顺便提一句,你给org_apiinteg_assets加了PACK_KEYS=1,这个是用来压缩索引节省空间的,对COUNT(*)的执行逻辑没影响,因为它根本碰不到索引。哪怕你的表没有主键,只要是MyISAM引擎,不带WHERE的COUNT(*)都是直接读元数据。
换成InnoDB的话:COUNT(*)得"实打实"统计
如果把你的表改成InnoDB引擎,情况就完全反转了。这是因为InnoDB是事务型引擎,支持多版本并发控制(MVCC)——简单说就是不同事务看同一张表,可能看到不一样的数据状态(比如一个事务插入了数据还没提交,另一个事务是看不到这些数据的)。
这种情况下,InnoDB没办法像MyISAM那样维护一个全局统一的总行数,它只能通过扫描索引或者全表来统计行数:
- 如果你的查询带WHERE条件,InnoDB会挑最合适的索引来扫描,统计符合条件的行数;
- 如果是不带WHERE条件的
COUNT(*),InnoDB会选最小的非聚簇索引来遍历(因为索引比全表小,扫描更快),要是没有非聚簇索引,就只能扫全表了。
比如把你的assessmentinstances改成InnoDB,执行COUNT(*)的时候,因为它只有主键(聚簇索引),MySQL就只能扫描整个聚簇索引来逐行统计,数据量越大,速度越慢,属于O(n)级别的操作。
核心差异一句话总结
- MyISAM不带WHERE的
COUNT(*):读预存元数据,快得飞起,但不支持事务一致性; - InnoDB不带WHERE的
COUNT(*):扫索引/全表统计,速度随数据量增长变慢,但能保证事务内看到的行数是一致的; - 带WHERE条件的
COUNT(*):两者逻辑类似,都是扫描合适索引统计,差异不大。
内容的提问来源于stack exchange,提问作者Bentaye

