explain的rows是优化器基于过时统计信息的预估,非实际扫描行数;真实值需用explain analyze查看actual rows,且须满足mysql≥8.0.18、select语句及process权限。

MySQL优化器不会“保证”选到绝对最优的执行计划,它只在有限时间内,基于当前统计信息和成本模型,选出它估算成本最低的那个——而这个估算结果,可能和实际运行效果差很远。
为什么EXPLAIN显示的rows和实际扫描行数差十倍?
因为优化器依赖的统计信息过时或不准。比如ANALYZE TABLE没跑过,或者表数据突增/突减后未刷新,优化器仍按旧的行数、索引基数(cardinality)估算成本。尤其对大表或高频写入场景,innodb_stats_auto_recalc默认关闭,innodb_stats_persistent又可能缓存过期值。
- 检查方式:
SHOW TABLE STATUS LIKE 'your_table'看Rows字段是否明显偏离真实值 - 强制更新:
ANALYZE TABLE your_table(线上慎用,会加MDL锁) - 更稳妥做法:在低峰期用
INFORMATION_SCHEMA.STATISTICS查CARDINALITY,对比字段唯一值数量是否合理
type=ALL却没走索引,是优化器“犯傻”吗?
不是犯傻,是它算出来全表扫描更便宜。常见原因有三类:
- WHERE条件用了函数或类型转换,比如
WHERE DATE(create_time) = '2026-01-01'或WHERE user_id = '123'(字段是INT但传了字符串),导致索引失效 - 索引选择性太低,比如性别字段建了索引,但
SELECT * FROM t WHERE gender = 'M'命中50%行,优化器认为回表成本 > 全表扫描 - 统计信息误判:比如某值占比90%,优化器以为范围扫描要读90万行,干脆选ALL
这时EXPLAIN FORMAT=JSON里的"filtered"字段比rows更关键——它表示该条件下预估的过滤率,若远低于实际,说明统计失真。
JOIN顺序不对,怎么让优化器“听劝”?
优化器默认用贪心算法评估连接顺序,但对复杂JOIN(≥4张表)或非等值条件容易出错。人工干预优先级:先改SQL写法,再用Hint,最后调配置。
- 把小结果集表放前面:显式用
STRAIGHT_JOIN强制顺序,如SELECT STRAIGHT_JOIN ... FROM small_table s JOIN large_table l ON ... - 避免隐式笛卡尔积:确认所有JOIN都有ON条件,否则优化器可能放弃重排
- 禁用危险优化:设
optimizer_switch='block_nested_loop=off'可防止BNL在内存不足时退化成磁盘临时表
注意:USE INDEX或FORCE INDEX只作用于单表,对JOIN顺序无影响;真正控制顺序的是表在FROM子句中的位置或STRAIGHT_JOIN。
最常被忽略的点:优化器的成本模型本身可调。比如SSD磁盘的io_block_read_cost默认是1.0,实际可能是0.2,不调就会高估索引I/O代价,倾向全表扫描——这比加索引或改SQL更隐蔽,也更难排查。











