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

InnoDB多版本控制:揭秘MVCC核心机制

这段内容来自 MySQL 官方文档第 17.3 节《InnoDB 多版本控制》(Multi-Versioning),深入讲解了 InnoDB 如何通过“多版本并发控制(MVCC)”实现高并发读写和一致性读(Consistent Read)

这是理解 InnoDB 高性能、高并发能力的核心机制之一。我们来一步步拆解并通俗地理解它。


🔷 一、什么是“多版本控制”(Multi-Versioning)?

简单说:InnoDB 不是直接覆盖旧数据,而是保留多个版本的数据,让不同事务看到“自己该看到的版本”。

🎯 目的:

  • 实现 非阻塞的一致性读(Consistent Nonlocking Reads):读操作不加锁,也不会被写操作阻塞。
  • 支持 事务回滚(Rollback)
  • 支持 MVCC(多版本并发控制)

🔷 二、InnoDB 是如何实现多版本的?

1. 每行数据都加了三个隐藏字段

InnoDB 在存储每一行数据时,自动添加三个隐藏列(你查 SELECT * 看不到,但内部存在):

隐藏字段大小作用
DB_TRX_ID6字节记录 最后修改这行的事务 ID(插入或更新)
• 删除 = 更新 + 标记删除位
DB_ROLL_PTR7字节“回滚指针”,指向 undo log 中的一条记录
• 这条 undo log 存着这行“之前长什么样”
DB_ROW_ID6字节自增的行 ID
• 如果表没有主键,InnoDB 用它来构建聚簇索引

✅ 举例:
你更新了一行,InnoDB 不是直接改原数据,而是:

  1. 把旧数据写入 undo log
  2. 更新当前行,并更新 DB_TRX_IDDB_ROLL_PTR
  3. 新版本指向旧版本的 undo log,形成“版本链”

2. Undo Log(回滚日志)与 Rollback Segment(回滚段)

  • Undo Log:记录数据“被修改前的样子”,用于:

    • 回滚事务(ROLLBACK
    • 构建历史版本(供一致性读使用)
  • Rollback Segment(回滚段):一个数据结构,管理所有的 undo log。

    • 存储在 undo tablespace(撤销表空间)中。
两种类型的 Undo Log:
类型用途何时可删除
Insert Undo Log只用于事务回滚(因为插入前这行不存在)事务一提交就可以删
Update Undo Log用于回滚 + 一致性读(构建旧版本)必须等到没有事务再需要它时才能删

⚠️ 重点:如果长时间不提交事务(尤其是只读事务),InnoDB 就不能清理 update undo log,导致 undo 表空间不断膨胀

📌 建议:即使是只读事务,也要及时提交,避免 undo 日志堆积。


🔷 三、MVCC 是如何工作的?(一致性读)

📌 场景:

  • 事务 A 开始查询某行。
  • 事务 B 在此期间修改并提交了这行。
  • 事务 A 再次查询,看到的还是修改前的数据(可重复读)。

✅ InnoDB 怎么做到的?

  1. 事务 A 开始时,InnoDB 给它分配一个 “快照”(snapshot),记录当前已提交的事务 ID 列表。
  2. 当事务 A 读取某行时:
    • 检查该行的 DB_TRX_ID
    • 如果这个事务 ID 在“快照”之后(即未提交或在 A 之后开始),就通过 DB_ROLL_PTR 找到 undo log
    • 从 undo log 中重建“事务 A 开始时”的那个版本
  3. 这样,事务 A 看到的就是“一致性视图”,不受其他事务干扰。

🔍 这就是 REPEATABLE READ 隔离级别的核心实现!


🔷 四、删除操作的特殊处理:Purge(清除)

❓ 问题:

你执行了 DELETE FROM t WHERE id=1;,数据立刻消失了吗?

❌ 答案:不是!

InnoDB 的删除是“延迟物理删除”:

  1. 先标记这行“已删除”(delete-marked)
  2. 等到 没有事务再需要这个版本时,由一个后台线程(purge thread)真正删除它
  3. 这个过程叫 Purge

✅ 好处:不影响正在运行的事务读取旧版本。

⚠️ 风险:如果 插入和删除速度很快且接近,purge 线程可能“追不上”,导致大量“死行”堆积,表越来越大,性能下降(I/O 瓶颈)。

🛠️ 解决方案:

  • 调整参数:innodb_max_purge_lag
    • 增加 purge 线程的优先级,让它更快清理死行
  • 控制写入速率(throttle)
  • 定期优化表(OPTIMIZE TABLE

🔷 五、二级索引(Secondary Index)的多版本处理

这是个关键区别

特性聚簇索引(Clustered Index)二级索引(Secondary Index)
是否有隐藏字段(DB_TRX_ID, DB_ROLL_PTR)✅ 有❌ 没有
是否原地更新(in-place update)✅ 是❌ 否

❓ 那么问题来了:

二级索引怎么支持 MVCC?

✅ 答案:通过“回查聚簇索引”

场景:

你有一个二级索引 idx_name,事务 A 查询 WHERE name='Alice'

  1. InnoDB 先在二级索引中找到 name='Alice' 的记录。
  2. 但这条记录没有 DB_TRX_ID
  3. 所以 InnoDB 必须:
    • 通过主键 ID 回到 聚簇索引
    • 检查聚簇索引中该行的 DB_TRX_ID
    • 判断是否需要从 undo log 构建旧版本

📌 这就是为什么“覆盖索引”(Covering Index)很重要:

  • 如果查询所需字段都在二级索引中(如 SELECT name FROM t WHERE name='Alice'),可以直接返回,不用回表
  • 否则必须回表查聚簇索引,性能下降

🔍 特殊优化:Index Condition Pushdown (ICP)

即使需要回表,MySQL 也可以优化:

  • 如果 WHERE 条件中有部分可以用二级索引判断(如 name='Alice' AND age>25
  • MySQL 会把 age>25 推给存储引擎
  • InnoDB 先用索引判断 age>25 是否成立
  • 只有成立时才回表查聚簇索引

✅ 好处:减少不必要的回表操作,提升性能。


🔷 总结:InnoDB 多版本控制的核心要点

机制作用
隐藏字段
DB_TRX_ID, DB_ROLL_PTR, DB_ROW_ID
记录每行的版本信息和回滚路径
Undo Log + Rollback Segment存储旧版本数据,支持回滚和一致性读
MVCC多事务并发时,各自看到“自己的快照”,互不阻塞
Purge 机制延迟删除,保证一致性读,但需监控性能
二级索引无版本信息查二级索引后必须回表查聚簇索引来判断可见性
ICP 优化减少不必要的回表

✅ 一句话总结:

InnoDB 通过“多版本控制”(MVCC),利用隐藏字段、undo log 和聚簇索引,实现了非阻塞的一致性读和事务隔离,既保证了数据一致性,又极大提升了并发性能。理解这一机制,是掌握 InnoDB 高并发能力的关键。

💡 类比:就像 Git 版本控制,每个事务看到的是数据库在某个时间点的“快照”,而不是实时的、可能正在被修改的状态。


文章转载自:

http://GHfwgbbV.qjghx.cn
http://8cNwjrrV.qjghx.cn
http://oiKezCVw.qjghx.cn
http://496uTkeZ.qjghx.cn
http://znMXO9J6.qjghx.cn
http://PYBwTGYD.qjghx.cn
http://7Wq8SZu5.qjghx.cn
http://anOpev4s.qjghx.cn
http://szGueuB0.qjghx.cn
http://e0ObmCtL.qjghx.cn
http://JZKrUhup.qjghx.cn
http://mo8EQZlq.qjghx.cn
http://FlJCWyU6.qjghx.cn
http://rBhbU5Fu.qjghx.cn
http://pqTNcOVx.qjghx.cn
http://hdy9TRAJ.qjghx.cn
http://4DiTBJWd.qjghx.cn
http://yy1tZobX.qjghx.cn
http://SFwgyWvY.qjghx.cn
http://OxEZC9dP.qjghx.cn
http://LsA1lnlb.qjghx.cn
http://7kyMZJWS.qjghx.cn
http://1IXaqTgl.qjghx.cn
http://7p40eQwB.qjghx.cn
http://fzMLfwRn.qjghx.cn
http://lLpD9HTl.qjghx.cn
http://3MDgMtNL.qjghx.cn
http://vBdhSpKY.qjghx.cn
http://Vx3ktaA1.qjghx.cn
http://GeNC8V5N.qjghx.cn
http://www.dtcms.com/a/387244.html

相关文章:

  • SpringMVC 系列博客(二):核心功能深入 —— 请求映射、返回值与参数绑定
  • HTTPS报文在SSL/TLS证书安全隧道传输的原理
  • 线性回归与 Softmax 回归技术报告
  • 不同团队如何选GIS软件?ArcGIS Pro、GISBox与SuperMap优劣势及适用方案
  • 静态标签云
  • AI解决企业内训之痛-智能企业内训平台解决方案
  • 容器化部署番外篇之docker网络通信06
  • Windows安装ES8.10流程及安装过程中出现的问题
  • 【工具代码】使用Python截取(切割)视频片段,截取视频中的音频,截取音频片段
  • Linux --- 权限
  • netty集成protobuf
  • ORA-12514:TNS:监听程序当前无法识别连接描述符中请求的服务
  • io_uring最简单的实例io_uring-test.c分析
  • 15.Linux时间管理
  • Linux 系统中的 Crond 服务:定时任务管理全指南
  • JDBC学习笔记
  • LoRA翻译
  • Linux 内存管理章节十五:内核内存的侦探工具集:深入Linux内存调试与检测机制
  • Mysql-主从复制与读写分离
  • bevformer 網絡結構
  • MySQL 基础与实战操作
  • 系统架构设计(二)
  • 【Day 58】Redis的部署
  • UVM验证工具--gvim
  • 《C++ spdlog高性能日志库快速上手》
  • 代码随想录学习(二)——二分查找
  • 【代码随想录day 27】 力扣 53. 最大子序和
  • Zynq开发实践(SDK之第一个纯PS工程)
  • 【Spring生态】Spring Cloud
  • HarmonyOS应用拉起系列(三):如何直接拉起腾讯/百度/高德地图进行导航