主从执行计划不一致90%以上源于统计信息未对齐:innodb_stats_auto_recalc从库关闭导致统计停更,需主从均设on并持久化;optimizer_switch参数必须逐字符一致;read_only不阻临时表,须启用super_read_only。

主从统计信息不同步导致执行计划分裂
90%以上的“同SQL不同执行计划”问题,根源不是SQL写法或索引缺失,而是innodb_stats_auto_recalc在主库开了、从库关了。一旦关闭,InnoDB就不再自动更新mysql.innodb_table_stats里的n_rows和last_update,哪怕表已增删百万行,优化器仍按旧基数估算成本。
检查方法:SELECT @@innodb_stats_auto_recalc;,主从必须都返回ON;若为OFF,仅SET GLOBAL不持久——必须同步改配置文件并重启(或用mysqld --initialize-insecure重置后重新配)。
-
innodb_stats_persistent = OFF时,统计信息不落盘,重启即丢,innodb_stats_auto_recalc实际失效 -
FLUSH TABLES WITH READ LOCK会清空stat_modified_counter,让自动收集永远达不到“总行数 × 10%”触发阈值 - 验证差异:直接查
SELECT database_name, table_name, n_rows, last_update FROM mysql.innodb_table_stats WHERE table_name = 'your_table';,对比主从last_update时间差
optimizer_switch参数不一致直接改写优化器逻辑
optimizer_switch是逗号分隔的开关字符串,控制着索引合并、哈希连接、物化临时表等底层行为。哪怕只有一项开关不同(比如index_merge=on vs index_merge=off),优化器就可能选完全不同的访问路径——EXPLAIN里key字段看似一样,但实际是否启用索引合并、是否下推条件,全由它决定。
检查命令:SELECT @@optimizer_switch;,主从输出必须逐字符一致;常见坑点包括:
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
- MySQL 5.7默认开启
derived_merge=on,8.0默认off,跨版本部署易踩坑 - 备份恢复后未手动同步
optimizer_switch,尤其Percona分支与官方版默认值有差异 - 某些ORM或中间件会动态设置
SET SESSION optimizer_switch = '...',需确认连接层是否覆盖了全局值
read_only=ON无法阻止临时表污染优化器决策
从库设了read_only = ON,但CREATE TEMPORARY TABLE、INSERT INTO ... SELECT隐式生成的临时表仍可执行。这些表不写binlog,却会参与JOIN估算——优化器看到“中间结果集只有10行”,实际运行时因数据倾斜膨胀到10万行,执行计划当场失效。
监控信号:SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';,若从库该值持续上涨(尤其报表类SQL后),基本可断定临时表在干扰统计。
- 真正只读需两步:
SET GLOBAL read_only = ON;→SET GLOBAL super_read_only = ON;(后者才禁所有写操作,含临时表) - 应用层必须规避:
SELECT ... INTO OUTFILE、SELECT ... INTO DUMPFILE,这类语句在从库执行等于主动绕过只读保护 - 临时救急可查
INFORMATION_SCHEMA.PROCESSLIST过滤Command = 'Query'且Info含TEMPORARY的会话,定位源头
LIMIT和ORDER BY组合触发优化器临场改判
EXPLAIN显示走IndexRTUser,实际执行却扫了IndexCreateTime,本质是优化器在评估“取1行最快路径”时,误判了WHERE UserId = ?的过滤效果。当UserId对应记录分散在时间维度上,LIMIT 1 + ORDER BY CreateTime DESC会让优化器放弃等值索引,转而选择能天然满足排序的索引——哪怕要扫描更多行。
典型表现:rows预估极小(如100),但Rows_examined慢日志里高达几十万;Extra字段出现Using where; Using filesort却没在EXPLAIN里体现。
- 验证方式:去掉
LIMIT 1再EXPLAIN,看是否回归预期索引;或加FORCE INDEX (IndexRTUser)强制走原路径 - 根治方案:建覆盖索引
KEY idx_user_status_time (UserId, ApplyStatus, IfDel, CreateTime),让WHERE+ORDER BY全部命中 - 注意:MySQL 8.0+引入直方图后,
ANALYZE TABLE需配合WITH VALIDATION才能更新列级分布统计,否则仍可能误判选择率
SHOW CREATE TABLE相同、EXPLAIN输出相似、甚至optimizer_switch字符串肉眼比对无差别,但stat_modified_counter归零、super_read_only未开、直方图未采集,任何一个都足以让优化器彻底转向。










