必须开启 innodb_stats_persistent=on,否则重启后统计信息丢失导致执行计划突变;mysql 8.0 默认开启但需验证并强制持久化设置,同时关闭自动重算、按表粒度控制、定时执行 analyze table 并调高采样页数或建直方图。

必须开启 innodb_stats_persistent=ON,否则重启后统计信息丢失,执行计划大概率突变。
检查并强制开启 innodb_stats_persistent
MySQL 8.0 默认是 ON,但生产环境不能依赖默认值——配置可能被覆盖、误改或在容器启动时被重置。务必验证:
- 执行
SHOW VARIABLES LIKE 'innodb_stats_persistent';,返回值必须是ON - 若为
OFF,立即执行SET PERSIST innodb_stats_persistent = ON;(写入mysqld-auto.cnf,优先级高于my.cnf) - 该参数设为 ON 后不可动态关闭;设为 OFF 必须重启 MySQL 才生效,所以别试
关掉 innodb_stats_auto_recalc,改用人工控制时机
默认开启的自动重算会在表数据变更超 10% 时触发,但实际容易误判:小表微小变更就反复重算,大表巨量写入却迟迟不更新,导致统计滞后或后台线程争抢资源。
- 全局关闭:
SET PERSIST innodb_stats_auto_recalc = OFF; - 对核心热表(如
orders)显式启用:ALTER TABLE orders STATS_AUTO_RECALC = 1; - 对低频冷表(如
config_cache)明确禁用:ALTER TABLE config_cache STATS_AUTO_RECALC = 0; - 所有
ANALYZE TABLE统一走定时任务,在凌晨低峰期执行,例如:ANALYZE TABLE orders, users;
调高 innodb_stats_persistent_sample_pages 并配合直方图
默认值 20 对千万级以上表严重不足,尤其字段分布倾斜(如状态码、区域 ID)时,Cardinality 被低估,优化器直接弃用索引。
- 中等表(100 万–500 万行):设为
50~100 - 大型热表(千万行以上):设为
100~200,但需监控ANALYZE TABLE耗时是否超 30 秒 - 若调到
200仍不准(比如SELECT COUNT(DISTINCT status) FROM orders和n_diff_pfx01值相差 5 倍以上),说明分布太复杂(含大量NULL、空字符串、JSON 字段),这时应建直方图而非硬调采样页数 - 创建等深直方图:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, created_at; - 注意:
ANALYZE TABLE不是一劳永逸——批量导入、逻辑删除、历史归档后必须重新触发,它应和 DDL 一样纳入上线 checklist
真正容易被忽略的是:直方图只补充列级分布,不替代表级统计;而 ANALYZE TABLE 的锁行为(持有 read only 锁 + flush lock)会让长事务阻塞其执行,必须提前评估业务窗口。











