当前位置: 首页 > news >正文

MySQL InnoDB持久化统计信息详解

这段内容是 MySQL InnoDB 存储引擎关于“持久化优化器统计信息(Persistent Optimizer Statistics)”的详细配置说明,属于数据库性能调优中非常核心和深入的部分。

我会用 清晰、结构化、通俗易懂 的方式,为你逐层解读这一大段文档的核心思想、关键机制和实际应用价值。


🎯 一、一句话总结

InnoDB 的“持久化统计信息”功能,就是让数据库记住每张表的数据分布特征(比如有多少行、索引有多“散”),并把这些信息保存到磁盘上,避免每次重启后重新估算,从而保证 SQL 执行计划稳定、可预测。


🔁 二、为什么需要“持久化统计”?—— 对比旧模式

旧模式(非持久化)新模式(持久化) ✅
每次 MySQL 重启,统计信息清空统计信息保存在磁盘上,重启不丢
下次访问表时重新采样计算直接读取上次保存的结果
可能每次采样结果不同 → 执行计划漂移执行计划更稳定
查询性能忽快忽慢性能更可预期

📌 举个例子:

  • 昨天你的 SQL 走了索引,很快。
  • 今天重启 MySQL 后,采样结果变了,优化器觉得“索引不值得走”,于是全表扫描 → 变慢了。

这就是 执行计划不稳定 的典型问题。持久化统计就是为了解决这个问题。


🔧 三、如何开启持久化统计?

✅ 全局开启(默认已开启)

SET GLOBAL innodb_stats_persistent = ON;

⚠️ 这个参数从 MySQL 5.6 开始默认就是 ON,所以大多数情况下你不需要手动设置。

✅ 表级开启(更灵活)

你可以在建表或改表时指定:

CREATE TABLE t1 (id INT PRIMARY KEY,name VARCHAR(100),KEY idx_name(name)
) ENGINE=InnoDBSTATS_PERSISTENT=1        -- 启用持久化统计STATS_AUTO_RECALC=1       -- 数据变化超10%自动更新统计STATS_SAMPLE_PAGES=25;    -- 采样25页数据来估算

💡 即使全局关闭了持久化,也可以通过 STATS_PERSISTENT=1 单独为某张表开启。


🔄 四、自动更新统计:innodb_stats_auto_recalc

默认行为:

  • 当一张表有 超过 10% 的数据被修改(INSERT/UPDATE/DELETE)后,
  • 并且 innodb_stats_auto_recalc = ON(默认 ON),
  • InnoDB 会在后台异步自动执行 ANALYZE TABLE 来更新统计信息。

⚠️ 注意:

  • 是“异步”的,所以不会立刻更新,可能延迟几秒。
  • 如果你需要立刻更新统计信息(比如刚导入大量数据),应该手动运行:
ANALYZE TABLE your_table_name;

这会同步地重新计算统计信息。

❌ 如果关闭自动更新:

SET GLOBAL innodb_stats_auto_recalc = OFF;

那你就要自己负责在大批量 DML 操作后手动执行 ANALYZE TABLE,否则优化器可能基于过时的统计做出错误决策。


📊 五、采样页数控制:STATS_SAMPLE_PAGESinnodb_stats_persistent_sample_pages

核心概念:随机采样(Random Dive)

InnoDB 不会扫描整张表来统计索引的“选择性”(cardinality),而是:

  • 随机读取若干个索引页(默认 20 页)
  • 通过这些样本估算整个索引的基数(不同值的数量)

参数说明:

参数作用
innodb_stats_persistent_sample_pages全局默认采样页数(默认 20)
STATS_SAMPLE_PAGES可以为单个表单独设置采样页数

⚖️ 权衡:准确 vs 速度

采样页数多(如 100)采样页数少(如 5)
✅ 统计更准确 → 执行计划更好❌ 统计不准 → 可能选错索引
ANALYZE TABLE 更慢ANALYZE TABLE 更快

📌 建议

  • 小表:保持默认(20)
  • 大表或统计不准导致性能问题:提高到 50~100
  • 高频执行 ANALYZE TABLE 的场景:适当降低

🗑️ 六、删除标记记录是否计入统计?innodb_stats_include_delete_marked

背景问题:

InnoDB 的删除是“标记删除”——数据还在,只是加了个“已删除”标记。

默认情况下,统计信息计算时会忽略这些“已删除但未清理”的记录

问题来了:

  • 事务 A 删除了 90% 的数据,但还没提交。
  • 事务 B 此时做 ANALYZE TABLE,发现表几乎空了 → 生成一个“适合小表”的执行计划。
  • 但事务 A 回滚了,数据还在!
  • 结果:执行计划错得离谱!

解决方案:

SET GLOBAL innodb_stats_include_delete_marked = ON;

✅ 开启后:统计信息会把“已标记删除”的记录也算进去,避免因未提交事务导致统计失真。

📌 适用场景:高并发写入 + 频繁删除的系统。


🗃️ 七、统计信息存在哪?—— 两张系统表

InnoDB 把持久化统计信息存入以下两张表:

1. mysql.innodb_table_stats —— 表级统计

字段含义
database_name数据库名
table_name表名(或分区名)
last_update最后更新时间 ✅(重要!)
n_rows表的行数估计
clustered_index_size主键索引占用页数
sum_of_other_index_sizes其他索引总页数

2. mysql.innodb_index_stats —— 索引级统计

字段含义
index_name索引名
stat_name统计项名称(如 n_diff_pfx01
stat_value统计值
sample_size本次统计采样了多少页
stat_description描述(如 a,b 表示统计的是 a 和 b 列)

🔍 八、关键统计项:n_diff_pfxNN —— 索引基数(Cardinality)

这是优化器选择索引的核心依据

含义:

  • n_diff_pfx01:索引第 1 列的不同值数量
  • n_diff_pfx02:前 2 列组合的不同值数量
  • n_diff_pfxN:前 N 列组合的不同值数量

示例解析(来自文档中的 t1 表):

| index_name | stat_name    | stat_value | stat_description |
|------------|--------------|------------|------------------|
| PRIMARY    | n_diff_pfx01 | 1          | a                |
| PRIMARY    | n_diff_pfx02 | 5          | a,b              |
| i1         | n_diff_pfx01 | 1          | c                |
| i1         | n_diff_pfx02 | 2          | c,d              |
| i1         | n_diff_pfx03 | 2          | c,d,a            |
| i1         | n_diff_pfx04 | 5          | c,d,a,b          |
分析:
  • 主键 (a,b)a 只有 1 个值,但 (a,b) 有 5 个不同组合 → 唯一性强
  • 二级索引 i1(c,d):虽然定义是 (c,d),但 InnoDB 会自动加上主键 (a,b) → 实际是 (c,d,a,b)
    • 所以有 n_diff_pfx03n_diff_pfx04
    • c 只有 1 个值 → 选择性极差,不适合做单列索引

📌 优化器就是靠这些 n_diff_pfx 值来判断:走哪个索引更高效。


🛠️ 九、高级技巧:手动修改统计信息(慎用!)

你可以直接更新 mysql.innodb_index_stats 来“欺骗”优化器:

UPDATE mysql.innodb_index_stats 
SET stat_value = 1000000 
WHERE table_name = 'orders' AND stat_name = 'n_rows';-- 必须执行,否则不生效
FLUSH TABLE orders;

用途:

  • 测试不同执行计划
  • 强制优化器选择某个索引(比如你知道数据分布特殊)
  • 排查执行计划问题

⚠️ 警告:仅限测试环境!生产环境乱改可能导致严重性能问题。


📈 十、如何查看索引大小?(实用 SQL)

-- 查看每个索引占了多少页和字节
SELECT index_name,SUM(stat_value) AS pages,SUM(stat_value) * @@innodb_page_size AS size_bytes
FROM mysql.innodb_index_stats 
WHERE table_name = 't1' AND stat_name = 'size' 
GROUP BY index_name;

输出示例:

+------------+-------+-------------+
| index_name | pages | size_bytes  |
+------------+-------+-------------+
| PRIMARY    | 1     | 16384       |
| i1         | 1     | 16384       |
| i2uniq     | 1     | 16384       |
+------------+-------+-------------+

💡 默认 InnoDB 页大小是 16KB(16384 字节)


✅ 十一、最佳实践建议

场景建议
所有生产环境✅ 确保 innodb_stats_persistent=ON
大表频繁修改STATS_AUTO_RECALC=1,自动更新
批量导入/删除后✅ 手动 ANALYZE TABLE
执行计划异常🔍 查 last_updaten_diff_pfx 是否过时或不准
统计不准📈 增加 STATS_SAMPLE_PAGES
ANALYZE TABLE 太慢🐢 适当减少采样页数
高并发删除✅ 考虑开启 innodb_stats_include_delete_marked
调试执行计划🧪 可临时修改 innodb_index_stats(测试环境)

🧩 总结:核心要点图谱

持久化统计 (ON by default)
│
├── 存在哪? → mysql.innodb_table_stats + mysql.innodb_index_stats
│
├── 何时更新?
│   ├── 自动:数据变化 >10% 且 innodb_stats_auto_recalc=ON(异步)
│   └── 手动:ANALYZE TABLE(同步,立即生效)
│
├── 采样多少? → STATS_SAMPLE_PAGES(越大越准,越慢)
│
├── 删除的数据算不算? → innodb_stats_include_delete_marked
│
└── 能不能骗优化器? → 可手动改统计表 + FLUSH TABLE(高级玩法)

如果你有具体的性能问题,比如:

  • “为什么这个 SQL 有时候走索引,有时候不走?”
  • ANALYZE TABLE 太慢怎么办?”
  • “如何判断统计信息是否过时?”

欢迎继续提问,我可以结合真实场景帮你分析。

http://www.dtcms.com/a/414828.html

相关文章:

  • 深入解析MySQL InnoDB Purge机制
  • 网站公司建立wordpress 分页不出来
  • 做英文的小说网站有哪些网站入口你会回来感谢我的
  • 基于神经网络的简单PID控制
  • 神经网络之仿射变换
  • PINN物理信息神经网络驱动的材料学二维热传导方程求解MATLAB代码
  • Docker(二)
  • 基于 PyTorch 完全从零手搓 GPT 混合专家 (MOE) 对话模型
  • 【企业架构】TOGAF架构标准规范-数据架构
  • 大疆无人机图传原理:从镜头到屏幕的实时传输解码之旅
  • Linux安全机制--应用层安全机制
  • 中国通信工业协会区块链专委会三届四次主任委员会议召开
  • 【记录】Ubuntu24.04安装Nvidia驱动
  • 做调查的网站知乎自学软件网站开发
  • 承包工地的网站微信插件图片转换wordpress
  • 基于PyTorch的CIFAR-10图像分类项目总结(2)
  • 网站建设绩效考评php笑话网站源码
  • 梁头网站建设wordpress手机模板插件
  • 浏览器发起http,到得到网页的整个过程
  • IntelliJ IDEA 制表符输出对不齐问题解决
  • 基于RAG的法律条文智能助手(方案篇)-实现与部署-微调与部署
  • Android-MVX技术总结
  • 辽宁学校网站建设请解释网站开发的主要流程
  • 【系统架构设计(39)】数据库控制技术
  • 深入浅出DBC:偏移量、精度、CRC与VCU数据流全解析
  • docker-私有仓库harbor
  • 网站如何做淘客肇庆市seo网络推广
  • Pycharm2025.2终端出现PS,无法切换到当前虚拟环境
  • 机器人动力学模型的建立方法有哪些 ?
  • 微信网站用什么软件做做网站的动态图片