mysql的explain中rows值常严重失真,因其依赖非实时、采样式统计且缺乏直方图与跨列相关性建模,实际扫描行数需以handler_read_*为准。

因为MySQL优化器依赖统计信息估算,而这些统计本身不实时更新、不覆盖全部数据分布细节。
统计信息不是实时刷新的
MySQL(尤其是InnoDB)通过采样页来估算索引基数、表行数等,ANALYZE TABLE 才会触发重新采样,但默认不会自动执行。这意味着:
- 刚插入10万行后立即
EXPLAIN,rows可能还显示旧的 2 万 - 大批量删除或导入后,
rows常严重偏离真实扫描行数 -
innodb_stats_auto_recalc开启时也只在表变更超 10% 才触发,且有延迟
直方图缺失导致分布盲区
MySQL 8.0+ 支持直方图,但默认不创建,而优化器对非均匀分布字段(如状态码、地区ID)完全无感知:
- 字段值集中在少数几个取值(比如
status IN (0,1)占99%),优化器仍按“均匀分布”算,rows可能高估百倍 - 手动创建直方图需显式执行
ANALYZE TABLE t UPDATE HISTOGRAM ON col,且仅对=、IN等简单谓词生效 - 范围查询(如
BETWEEN、>)基本不受益于直方图
索引统计无法反映实际过滤效果
rows 是优化器预测“可能需要检查多少行”,但它把索引查找和回表分离估算,忽略多条件组合的真实选择率:
- 两个独立索引字段
a=1 AND b=2,优化器可能分别估算a=1返回1000行、b=2返回500行,再相乘得50万——但实际交集可能只有3行 - 没有跨列统计信息(MySQL 不维护列相关性),也无谓词间逻辑关系建模
- 使用
FORCE INDEX或覆盖索引时,rows有时反而更不准,因为它绕过了原本的索引选择路径
真正影响执行效率的是实际执行时的 Handler_read_* 状态变量,而不是 EXPLAIN 的 rows;别把它当精确计数用,尤其在数据倾斜、未分析、复合条件场景下,偏差一个数量级都很常见。











