news 2026/4/23 13:52:31

进阶-InnoDB引擎-MVCC

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进阶-InnoDB引擎-MVCC

一、MySQL进阶

“当两个用户同时查看同一份数据,你希望看到的是‘当前状态’,还是‘事务开始时的状态’?”
—— MVCC(多版本并发控制),让数据库在高并发下依然保持“一致性”与“高效性”

为什么需要MVCC?——一场“读写冲突”的危机

在传统数据库中,读操作会阻塞写操作,写操作会阻塞读操作。例如:

  • 事务A:UPDATE orders SET status='shipped' WHERE order_id=100
  • 事务B:SELECT * FROM orders WHERE order_id=100

如果事务B在事务A未提交时执行,会发生什么?

  • 悲观锁:事务B必须等待事务A提交(锁表),导致性能下降
  • 乐观锁:事务B直接读取,但可能读到未提交数据(脏读)

💡2010年,某电商网站因锁表问题导致双11期间响应时间从50ms飙升至500ms,损失超百万!
这就是MVCC诞生的背景在不加锁的情况下,实现高并发读写

1. InnoDB引擎-MVCC

📌 什么是MVCC?

MVCC(Multi-Version Concurrency Control)是一种并发控制机制,它通过保存数据的多个版本,让读操作无需等待写操作,从而实现“读不阻塞写,写不阻塞读”

📌 两种读操作:

类型说明举例是否加锁
当前读(Current Read)读取最新数据(已提交)SELECT ... FOR UPDATE
UPDATE
DELETE
✅ 加锁(X锁)
快照读(Snapshot Read)读取事务开始时的快照SELECT *❌ 不加锁

💡关键点快照读是MVCC的核心,它利用历史版本实现非阻塞读。

MVCC的核心组件:三个隐式字段

InnoDB 通过三个隐藏字段(在表中不可见)实现MVCC

字段作用类型说明
DB_TRX_ID记录最近修改该行的事务ID6字节用于判断行是否对当前事务可见
DB_ROLL_PTR指向Undo Log的指针7字节用于找到历史版本
DB_ROW_ID隐藏的行ID(自增)6字节在索引中作为回表的唯一标

💡为什么需要这些字段?

  • DB_TRX_ID:判断行是否已提交
  • DB_ROLL_PTR:通过Undo Log找到旧版本
  • DB_ROW_ID:在索引中唯一标识行(即使主键未指定)

Undo Log:数据的“时间胶囊”

Undo Log 是 MVCC 的基础,它记录了数据修改前的旧值,用于:

  • 事务回滚
  • MVCC 快照读

📌 Undo Log 两种类型:

类型作用说明
INSERT记录插入的行用于事务回滚(删除时需恢复)
UPDATE记录修改前的旧值用于快照读和回滚(更新时需恢复)

💡Undo Log 的存储
由 InnoDB 的Undo Tablespace管理,可以配置多个 Undo Log 文件(MySQL 5.7+ 支持独立Undo表空间)。

数据版本链:Undo Log 的“链式存储”

当一行数据被多次修改时,Undo Log 会形成版本链

最新版本 → (DB_ROLL_PTR) → 旧版本 → (DB_ROLL_PTR) → 更旧版本 → ... → 初始版本

💡举例

  • 初始:balance=1000
  • 事务1:UPDATE balance=800→ 生成Undo Log(记录1000)
  • 事务2:UPDATE balance=500→ 生成Undo Log(记录800)

版本链:500 → 800 → 1000

ReadView:MVCC的“时间机器”

ReadView是事务开始时生成的快照,它决定了哪些版本对当前事务可见。

📌 ReadView 包含的关键信息:

字段说明
min_trx_id活跃事务最小ID(当前正在执行的事务ID)
max_trx_id活跃事务最大ID(当前正在执行的事务ID上限)
m_ids活跃事务ID列表(当前正在执行的事务ID集合)
def is_visible(trx_id, readview): if trx_id < readview.min_trx_id: # 已提交 return True elif trx_id >= readview.max_trx_id: # 未提交 return False else: # 在活跃事务列表中 if trx_id in readview.m_ids: # 未提交 return False else: # 已提交 return True

💡关键点

  • RR级别(可重复读):事务开始时生成ReadView,后续查询都使用同一ReadView
  • RC级别(读已提交):每次查询都生成新的ReadView

MVCC工作原理:一次事务的“时间穿越”

假设执行:

-- 事务A(ID=100) START TRANSACTION; SELECT * FROM accounts WHERE user_id=1; -- 快照读 UPDATE accounts SET balance=500 WHERE user_id=1; -- 当前读 COMMIT; -- 事务B(ID=101) START TRANSACTION; SELECT * FROM accounts WHERE user_id=1; -- 快照读

📌 MVCC如何工作?

  1. 事务A开始
    • 生成ReadView:min_trx_id=100, max_trx_id=102, m_ids=[100]
    • 执行SELECT:快照读,使用ReadView判断可见性
  2. 事务A执行UPDATE
    • 修改行,生成Undo Log(记录旧值:1000 → 500)
    • DB_TRX_ID更新为100
    • DB_ROLL_PTR指向Undo Log
  3. 事务B执行SELECT
    • 生成ReadView:min_trx_id=101, max_trx_id=102, m_ids=[101]
    • 读取行:DB_TRX_ID=100 < 101→ 可见(值为1000)

🌟效果:事务B在事务A未提交时,读到的是1000(事务A开始时的值),而非500(当前值)。

MVCC在不同隔离级别下的表现

隔离级别快照读可见性规则幻读说明
读未提交不使用MVCC直接读最新数据无MVCC
读已提交使用MVCC每次查询生成新ReadView无法避免不可重复读
可重复读使用MVCC事务开始时生成ReadView通过Next-Key Lock避免幻读
串行化不使用MVCC强制加锁无MVCC

💡为什么RR级别能避免幻读?
InnoDB 在RR级别下使用Next-Key Lock(记录锁 + 间隙锁),在读取时不仅锁住记录,还锁住间隙,防止其他事务插入新数据。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:30:18

打开软件提示找不到d3dx9_41.dll如何修复? 附免费下载方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华
网站建设 2026/4/23 12:12:09

基于SpringBoot网络安全教育网的设计与实现

博主主页&#xff1a;一点素材 博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬…

作者头像 李华
网站建设 2026/4/23 12:10:33

SEDA (Staged Event-Driven Architecture, 分阶段事件驱动架构

SEDA&#xff08;Staged Event-Driven Architecture&#xff0c;分阶段事件驱动架构&#xff09;是将复杂事件驱动应用拆解为多个通过队列连接的独立处理阶段&#xff0c;结合事件驱动与动态资源控制&#xff0c;以实现高并发、负载适配与模块化的架构范式&#xff0c;由 UC Be…

作者头像 李华