InnoDB 统计信息持久化机制:为什么 innodb_stats_persistent 会导致执行计划突变

在大促保障期,很多 DBA 最恐惧的事情莫过于:一条平时执行耗时稳定在 2 毫秒的核心交易 SQL,在毫无任何代码发布与配置变更的情况下,突然在某天下午的写入高峰期**“执行计划突变(Plan Regression)”**——优化器突然放弃了原本走得好好的二级索引,莫名其妙地转向了全表扫描,瞬间引发全库 CPU 打满与雪崩。
排查这种幽灵般的执行计划突变,绝大多数最终都指向了同一个被很多人忽视的底层内核参数:innodb_stats_persistent 与自动统计信息重新计算机制(Auto Recalculation)。
深入 InnoDB 的统计信息持久化内核,看清元数据采样的物理过程,是解决执行计划漂移问题的必修课。
— 查看 InnoDB 持久化统计信息系统表
SELECT table_name, n_rows, clustered_index_size, sum_of_other_index_sizes, last_update
FROM mysql.innodb_table_stats
WHERE table_name = 't_trade_order';
— 查看各个索引的具体统计采样数据 (基数 Cardinality 与叶子节点数)
SELECT table_name, index_name, stat_name, stat_value, sample_size, stat_description
FROM mysql.innodb_index_stats
WHERE table_name = 't_trade_order';
远古时代的混乱:非持久化统计与“重启玄学”
在 MySQL 5.6 之前,统计信息默认是非持久化的(innodb_stats_persistent = OFF)。统计数据仅仅临时保存在内存中。每次数据库实例重启、执行 SHOW TABLE STATUS、甚至表在内存中被换出重新打开时,InnoDB 都会在后台随机抓取 8 个叶子节点数据页(innodb_stats_transient_sample_pages = 8)进行粗略推算。
致命缺陷:由于每次随机抽样的 8 个数据页不同,算出来的基数(Cardinality)上下浮动极其剧烈。同一个查询,可能早上重启后走索引 A,下午由于一次元数据刷新又变成了走索引 B,系统稳定性完全处于“碰运气”的失控状态。
[InnoDB 统计信息持久化存储与自动重算触发流]
业务持续高频写入 (INSERT / UPDATE / DELETE)
│
▼
[累积变更行数超过 10% (innodb_stats_auto_recalc 触发阈值)]
│
▼
[后台异步工作线程唤醒: 随机抽样 20 个索引数据页 (innodb_stats_persistent_sample_pages)]
│
▼
[重新计算 Cardinality 基数并写入持久化系统表 mysql.innodb_index_stats]
│
▼
[优化器下一次编译 SQL 时读取新代价 ──▶ 突然发生执行计划跳水突变!]
持久化统计机制(innodb_stats_persistent = ON)
为了终结这种随机性,MySQL 引入了统计信息持久化。采样结果被固化在 mysql.innodb_table_stats 和 mysql.innodb_index_stats 两张事务表中,保证了实例重启后执行计划的确定性。
然而,默认开启的 自动重新计算机制(innodb_stats_auto_recalc = ON) 却埋下了一颗新的定时炸弹:
触发条件:10% 数据行变更
当表中的数据变更行数($D$)超过了表中总行数($N$)的 10% 时:$$D > 0.1 \\times N$$InnoDB 会在后台自动唤醒一个异步线程,重新对索引进行 20 个数据页的采样(innodb_stats_persistent_sample_pages = 20),并原子更新系统表中的元数据。
为什么自动采样会导致“大促前夜突变”?
— 生产级核心大表统计信息防漂移锁定配置
— 1. 针对核心交易大表,显式关闭自动重新计算,防止高峰期突发采样
ALTER TABLE t_trade_order
STATS_PERSISTENT = 1,
STATS_AUTO_RECALC = 0,
STATS_SAMPLE_PAGES = 100; — 将采样页数提升至 100~200,大幅提升基数精度
— 2. 在业务低峰期 (如凌晨 03:00) 通过定时任务手动触发高质量采样更新
ANALYZE TABLE t_trade_order;
工业级大促封网规范
为了确保大促期间系统执行计划的绝对稳定,存储团队必须严格执行以下三条纪律:
把统计信息的控制权牢牢掌握在工程师手中,坚决消除任何非受控的后台随机性,这是保障数据库内核稳定运行的坚实底线。
