持久化统计信息是什么
InnoDB 需要知道每个索引里大致有多少行、值的区分度如何,才能挑出执行计划,这类数据就是统计信息。MySQL 8.0 默认把它持久化到磁盘(innodb_stats_persistent 打开),实例重启后沿用而不再重新采样,计划因此更稳定。统计准不准,由「采样多少页」决定,采样页数就是拿准确度换代价的那个旋钮。
基本信息
| innodb_stats_persistent | 默认 ON,统计信息落盘,重启后不重新估算 |
|---|---|
| innodb_stats_auto_recalc | 默认 ON,表数据变动超过约一成时异步重算 |
| innodb_stats_persistent_sample_pages | 默认 20,每个索引采样多少页 |
| 表级子句 | 建表或改表时的 STATS_PERSISTENT、STATS_AUTO_RECALC、STATS_SAMPLE_PAGES |
| 存放位置 | mysql.innodb_table_stats 与 mysql.innodb_index_stats |
典型使用场景
- 重启后保持计划稳定:避免每次重启都重新采样导致执行计划漂移
- 关键表手动刷新:批量导入或大批删除后立刻重新统计
- 大表采样调优:默认采样页对超大表偏少,估算偏差大时按需上调
- 次要表降低成本:不重要的表调小采样页数,减少统计开销
- 排查选错索引:先确认统计是否过期,再决定要不要写提示
- 核对估算:查统计表里的数值与真实行数比对,定位失真环节
查看与调整
查看与调整
SHOW VARIABLES LIKE 'innodb_stats_persistent%'; # 查看持久化开关与采样页数
SHOW VARIABLES LIKE 'innodb_stats_auto_recalc'; # 确认自动重算是否开启
ALTER TABLE orders STATS_PERSISTENT=1, STATS_AUTO_RECALC=1, # 表级声明优先于全局设置
STATS_SAMPLE_PAGES=64; # 这张表每次采样 64 页
ANALYZE TABLE orders; # 立即重新统计,不等后台重算
SELECT table_name, n_rows, clustered_index_size # 查看落盘的表级统计
FROM mysql.innodb_table_stats WHERE database_name = 'app';
SELECT index_name, stat_name, stat_value # 查看各索引的基数与页数估算
FROM mysql.innodb_index_stats
WHERE database_name = 'app' AND table_name = 'orders';参数逐项读法
| 参数 | 默认值 | 影响 |
|---|---|---|
| innodb_stats_persistent | ON | 关闭后统计只存内存,重启与部分元数据操作会触发重新采样 |
| innodb_stats_auto_recalc | ON | 控制数据变动超阈值后是否自动重算 |
| innodb_stats_persistent_sample_pages | 20 | 调大更准但统计更慢,调小省事但估算会飘 |
| innodb_stats_transient_sample_pages | 8 | 只在使用非持久化统计时起作用 |
| STATS_SAMPLE_PAGES(表级) | 跟随全局 | 可按表的重要性分级,热点表给更大值 |
自动重算有阈值也有延迟:它按「变更行数占比超过约一成」触发,而且是异步在后台执行的。刚完成大批量变更的关键表,直接执行 ANALYZE TABLE 比等它自己重算更可控。
排查估算偏差时,先查 mysql.innodb_index_stats 里的统计值与真实行数对比,确认是采样式统计失真还是分布问题;列级直方图与索引基数各管一段,不要混为一谈。
采样页数调大会同时放大手动 ANALYZE 与后台重算的 IO 成本,写入频繁的大表上要按重要性分级设置,不要全局一刀切。