MyISAM的COUNT(*)快是因为它直接读取磁盘上存储的固定行数元数据(rec_count),无需扫描数据页、索引树或判断可见性,故为O(1)时间复杂度;但仅适用于无WHERE条件的全表统计,且MyISAM不支持事务和崩溃恢复,线上业务基本不用。

COUNT(*) 在 MyISAM 下快,是因为它真不查数据——只读磁盘上一个固定字段。
MyISAM 的 COUNT(*) 为什么能 O(1) 返回
MyISAM 引擎在 .MYI 文件头里存了一个叫 rec_count 的整数,每次 INSERT 或 DELETE 都原子更新它。执行无条件 COUNT(*) 时,MySQL 直接从这个元数据里取值,不读数据页、不走索引树、不判断可见性。
- 这个值在表未崩溃、未被
ALTER TABLE重建、且未重启后首次访问时,是精确的 - 如果执行过
OPTIMIZE TABLE或服务器刚启动,首次COUNT(*)可能触发一次全表扫描来重建计数器 - 只要加了
WHERE(哪怕WHERE 1),就立刻退化为实际扫描,这个“快”就失效了
MyISAM 的 COUNT(*) 不是万能的
它快的前提是:你用的是 MyISAM 引擎,且接受它的设计代价。
- 默认建表在 MySQL 5.7+ 和 8.0 中已是
InnoDB,SHOW CREATE TABLE t看ENGINE=才能确认 - MyISAM 不支持事务、行锁、MVCC、崩溃安全恢复——线上业务基本不用,只适合只读报表或日志归档场景
-
SHOW TABLE STATUS返回的Rows字段在 MyISAM 下是精确值,但别拿它当实时监控指标,因为并发写入下计数器可能短暂不一致(虽然概率低)
别把 InnoDB 的优化思路套到 MyISAM 上
给 InnoDB 加覆盖索引、用 USE INDEX、调 innodb_stats_sample_pages 这些手段,在 MyISAM 上完全无效——它压根不走索引扫描。
- MyISAM 的
COUNT(*)优化器不会看任何索引,也不受key_buffer_size影响 -
COUNT(1)、COUNT(pk)、COUNT(*)在 MyISAM 下执行计划和性能完全一致,都是读元数据 - 如果你在 MyISAM 表上看到
EXPLAIN显示type: index,说明你写了WHERE条件,已经掉出“秒出”路径了
真正容易被忽略的是:MyISAM 的“快”只存在于无条件全表统计这个极其狭窄的场景;一旦业务需要事务、写一致性或任意过滤条件,这个优势就归零,而代价早已埋在引擎选型里。











