报表统计“跳变”源于快照读时机失控:事务首次SELECT才建read view,若先写后查则快照晚于写操作;全表扫描时MVCC可能误判新提交记录为可见;information_schema.TABLES等估算值不具事务一致性。

报表统计结果在并发写入下出现“跳变”,不是COUNT出错,而是你没控制住快照读的创建时机。
事务启动 ≠ 快照创建
很多人以为 BEGIN 或 START TRANSACTION 就立刻建好了快照,其实不是。InnoDB 的一致性视图(read view)是在事务内**第一次执行 SELECT(含 COUNT(*))时才生成的**。如果事务先做了 UPDATE 或 INSERT,再查 COUNT(*),快照时间点就晚于那些写操作——此时其他事务刚提交的数据可能已被纳入统计,导致“多算”。
- 想让快照覆盖整个统计逻辑,必须用
START TRANSACTION WITH CONSISTENT SNAPSHOT显式声明 - 避免在事务开头混用写操作;如果必须更新,把统计语句提到最前面
- 用
SELECT @@transaction_isolation确认当前确实是REPEATABLE-READ
COUNT(*) 走全表扫描时容易突破快照边界
当 COUNT(*) 无法走索引、被迫全表扫描时,InnoDB 在遍历过程中可能遇到其他事务刚提交的新版本记录(尤其是二级索引+主键回表路径),而这些记录的 trx_id 恰好落在你的 read view 高水位附近,就会被误判为“可见”——这不是 bug,是 MVCC 在高并发下的行为边界。
- 用
EXPLAIN SELECT COUNT(*) FROM t看type是否为ALL;如果是,优先加覆盖索引,比如KEY idx_dummy (id)(哪怕只建单列主键字段的索引也能避免真正全表扫) - 不要依赖
WHERE 1=1或空条件触发优化;显式写WHERE id IS NOT NULL有时能帮优化器选对索引 - 大表慎用
COUNT(*),可考虑定期物化统计值到汇总表,用INSERT ... ON DUPLICATE KEY UPDATE维护
别拿 information_schema.TABLES 当真
SELECT TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_NAME='t' 返回的只是 InnoDB 的采样估算值,重启、ANALYZE TABLE 后才更新,且完全不遵循事务隔离级别。它和你的快照读毫无关系,误差常超 15%,报表校验中直接用等于自欺欺人。
- 该字段对 MyISAM 是精确的,但 InnoDB 永远不准
-
SHOW TABLE STATUS LIKE 't'中的Rows字段同理,仅作粗略参考 - 若真要快速估算,可用
SELECT COUNT(*) FROM t LIMIT 10000加采样逻辑,但别混进强一致场景
快照读的“一致性”是有前提的:事务内首次 SELECT 必须早于所有干扰写操作,且执行计划不能意外触发当前读路径。这两个条件漏掉任何一个,COUNT 出来的数就只是“那一刻看起来合理”,而不是“业务上可信赖”。


















