欢迎光临
我们一直在努力

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

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 亿行记录、占用 50 万个数据页的超大表,默认采样 20 个页仅仅覆盖了 0.004% 的物理数据!如果这 20 个页恰好落在了数据分布不均匀的聚集区,算出来的基数误差可能达到数十倍;
  • 高频写入期触发不可控刷新:大促开售时,秒杀写入瞬间触发了 10% 的变更红线。后台采样线程在写入高压下被唤醒,更新了错误的基数统计,导致优化器在业务洪峰期间突然将执行计划改写为全表扫描!
  • — 生产级核心大表统计信息防漂移锁定配置
    — 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;

    工业级大促封网规范

    为了确保大促期间系统执行计划的绝对稳定,存储团队必须严格执行以下三条纪律:

  • 核心大表显式关闭 STATS_AUTO_RECALC:禁止 InnoDB 在大促高并发写入期间自作主张地重新采样;
  • 调大关键表的采样页数(STATS_SAMPLE_PAGES):对核心大表设置为 100 到 300 页,消除随机局部倾斜导致的估算失真;
  • 大促封网前统一执行 ANALYZE TABLE 并固化基线:在大促前最后的低峰维护窗口,对全库核心表执行一次全量统计分析,并通过 SQL Plan Management(SPM)或 Optimizer Hints 锁定最优执行计划。
  • 把统计信息的控制权牢牢掌握在工程师手中,坚决消除任何非受控的后台随机性,这是保障数据库内核稳定运行的坚实底线。

    赞(0)
    未经允许不得转载:171主机测评 » InnoDB 统计信息持久化机制:为什么 `innodb_stats_persistent` 会导致执行计划突变
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址