news 2026/8/12 10:33:16

5.5 MVCC原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
5.5 MVCC原理

MySQL MVCC(多版本并发控制)原理完全指南

MVCC 是 InnoDB 存储引擎最核心的技术之一,也是 MySQL 能够在高并发场景下保持卓越性能的“秘密武器”。如果说锁是解决并发问题的“ brute force”(暴力手段),那 MVCC 就是“优雅的妥协”——让读写互不阻塞,在保证一致性的前提下最大化并发能力。

一、什么是 MVCC?

MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种不用加锁就能实现事务隔离的并发控制机制。它的核心思想非常简洁:

写操作不直接覆盖旧数据,而是生成一个新版本;读操作根据事务的隔离级别和时间戳,选择合适的版本来读

这样一来,读操作永远不用等待写操作,写操作也不用等待读操作,读写互不阻塞

生活类比:你在写一份文档,每隔一段时间保存一个历史版本。别人想读的时候,可以选择读最新版,也可以选择读某个历史版。你写你的,他读他的,互不干扰。

重要前提:InnoDB 的 MVCC仅在READ COMMITTED(RC)和REPEATABLE READ(RR)两个隔离级别下生效READ UNCOMMITTED直接读最新版本,SERIALIZABLE靠锁机制保证一致性,都不使用 MVCC。

二、MVCC 的三大核心组件

MVCC 的实现依赖于三个紧密配合的组件:隐藏字段 + Undo Log 版本链 + Read View

2.1 隐藏字段(Hidden Columns)

InnoDB 为每一行数据悄悄添加了三个“看不见”的隐藏字段:

隐藏字段长度核心作用
DB_TRX_ID6 字节最后修改该行的事务 ID,全局递增
DB_ROLL_PTR7 字节回滚指针,指向 Undo Log 中该行的上一个版本
DB_ROW_ID6 字节隐藏主键,当表没有主键和唯一非空索引时自动生成

关键认知DB_ROW_ID不是 MVCC 必需的,只有当表没有显式主键时才会用到。纯只读事务(只有 SELECT)不会分配事务 ID,其DB_TRX_ID默认为 0。

2.2 Undo Log 版本链(Version Chain)

Undo Log 是 MVCC 的“数据载体”。每一次对数据行的修改,都会生成一条对应的 Undo Log,通过DB_ROLL_PTR将所有历史版本串联成一条单向链表

版本链生成过程示例

-- ① 插入初始数据(事务ID=3)INSERTINTOuser(id,name,age)VALUES(1,'张三',20);-- 此时:DB_TRX_ID=3,DB_ROLL_PTR=NULL,版本链只有初始版本-- ② 第一次更新(事务ID=5)UPDATEuserSETage=21WHEREid=1;-- 生成 undo log(age=20, DB_TRX_ID=3)-- 当前行:age=21, DB_TRX_ID=5, DB_ROLL_PTR → undo log-- ③ 第二次更新(事务ID=8)UPDATEuserSETage=22WHEREid=1;-- 生成新的 undo log(age=21, DB_TRX_ID=5)-- 当前行:age=22, DB_TRX_ID=8, DB_ROLL_PTR → 新 undo log

最终形成的版本链结构:

当前行 (age=22, DB_TRX_ID=8) ↑ DB_ROLL_PTR Undo Log V2 (age=21, DB_TRX_ID=5) ↑ DB_ROLL_PTR Undo Log V1 (age=20, DB_TRX_ID=3) ↑ DB_ROLL_PTR NULL(初始版本)

2.3 Read View(读视图/一致性视图)

如果说 Undo Log 版本链是 MVCC 的“数据载体”,那么Read View 就是 MVCC 的“决策大脑”。它定义了当前事务能看到哪些数据版本、不能看到哪些数据版本。

Read View 的四个核心字段

字段含义
m_ids生成 Read View 时,当前所有活跃的未提交读写事务 ID 集合
min_trx_idm_ids中的最小值(最早的未提交事务)
max_trx_id生成 Read View 时,系统下一个要分配的事务 ID(非活跃事务最大值!)
creator_trx_id生成当前 Read View 的事务自己的 ID(只读事务为 0)

⚠️常见误区纠正max_trx_id不是活跃事务的最大值,而是下一个要分配的事务 ID。比如当前活跃事务 ID 是 2、3、5,那么max_trx_id = 6,而不是 5。这个认知错误会直接导致可见性判断完全错乱。

三、可见性判断算法(核心)

有了 Read View 和版本链,InnoDB 会按照固定规则,从版本链的最新版本开始,依次判断每个版本是否可见,直到找到第一个符合规则的版本。

判断规则(优先级从高到低)

对于版本链中某个版本,其 DB_TRX_ID = trx_id: ① 如果 trx_id == creator_trx_id → ✅ 可见(当前事务自己修改的版本,当然可见) ② 如果 trx_id < min_trx_id → ✅ 可见(该版本在生成 Read View 前已提交) ③ 如果 trx_id >= max_trx_id → ❌ 不可见(该版本的事务在生成 Read View 后才启动) ④ 如果 min_trx_id <= trx_id < max_trx_id → 判断 trx_id 是否在 m_ids 中: - 在 m_ids 中 → ❌ 不可见(该事务仍未提交) - 不在 m_ids 中 → ✅ 可见(该事务在生成 Read View 前已提交) ⑤ 如果当前版本不可见 → 沿着 DB_ROLL_PTR 找到上一个版本,重复上述判断

直观理解

条件结论
trx_id < min_trx_id该事务早就提交了→ 可见
trx_id in m_ids该事务还在运行→ 不可见
trx_id >= max_trx_id该事务未来才启动→ 不可见
trx_id == creator_trx_id自己改的→ 可见

四、RC 与 RR 的核心差异:Read View 生成时机

RC 和 RR 隔离级别的本质区别,就在于Read View 的生成时机不同

对比项RC(读已提交)RR(可重复读)
Read View 生成时机每次 SELECT都生成新的 Read View事务中第一次 SELECT时生成,整个事务复用
能否看到其他事务提交的修改✅ 能(每次查询都看到最新提交)❌ 不能(只看到事务启动前的数据)
不可重复读❌ 存在✅ 已解决
幻读(快照读)❌ 存在✅ 已解决(InnoDB 特殊实现)
并发性能更高稍低

4.1 RC 级别下的表现

RC 级别下,每次 SELECT 都生成新的 Read View,因此只能看到已经提交的事务修改(解决了脏读),但因为每次 Read View 都是最新的,会出现不可重复读

示例

时间会话A(RC)会话B(RC)
T1BEGINBEGIN
T2SELECT → age=20
T3UPDATE age=21(未提交)
T4SELECT → age=20(脏读解决)
T5COMMIT
T6SELECT → age=21(不可重复读!)

T6 时,会话A 生成了新的 Read View,会话B 已提交,所以看到了新数据。

4.2 RR 级别下的表现

RR 级别下,事务内第一次 SELECT 生成 Read View 后,整个事务复用,因此看不到其他事务在事务启动后提交的修改,解决了不可重复读。

示例

时间会话A(RR)会话B(RR)
T1BEGINBEGIN
T2SELECT → age=20(生成 Read View)
T3UPDATE age=21;COMMIT
T4SELECT → age=20(复用 Read View,看不到提交!)

T3 时会话B 虽然提交了,但会话A 复用第一次的 Read View,根据可见性规则,会话B 的trx_idm_ids中,因此修改不可见。

五、快照读 vs 当前读

MVCC 把 SELECT 操作分成了两种:

读类型对应 SQL读哪个版本是否加锁
快照读(Snapshot Read)普通SELECT读 Read View 指向的快照版本(历史数据)❌ 不加锁
当前读(Current Read)SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODEUPDATEDELETEINSERT读行的最新版本✅ 加锁

关键理解

  • 快照读:不加锁,只管读。RR 下读到事务快照建立时的数据版本,RC 下读到当前语句执行那一刻的快照。
  • 当前读:加锁,读最新版。每次必须拿到最新的数据,如果该行正被其他事务修改(未提交),会阻塞等待锁释放。
-- 快照读:不加锁,读快照SELECT*FROMuserWHEREid=1;-- 当前读:加锁,读最新SELECT*FROMuserWHEREid=1FORUPDATE;SELECT*FROMuserWHEREid=1LOCKINSHAREMODE;UPDATEuserSETname='Bob'WHEREid=1;-- 也是当前读DELETEFROMuserWHEREid=1;-- 也是当前读

六、MVCC 与锁的协作

MVCC 和锁不是替代关系,而是协作关系

冲突类型解决方案
读-写冲突MVCC(读写互不阻塞)
写-写冲突锁机制(行锁、间隙锁、Next-Key Lock)

在 RR 级别下,InnoDB 通过MVCC + Next-Key Lock的组合彻底解决了幻读问题:

  • MVCC 层面:整个事务复用同一个 Read View,看不到其他事务新增的行
  • 锁层面Next-Key Lock(记录锁 + 间隙锁)阻止其他事务在查询范围内插入新数据

七、MVCC 的局限性与注意事项

局限性说明
只能解决读写冲突写-写冲突仍然需要通过锁来解决
Undo Log 占用空间大量历史版本占用磁盘空间,需要 Purge 线程清理
长事务是杀手长事务导致 Undo Log 无法被清理,可能撑爆磁盘
不支持 DDLDDL 操作加表级锁,会阻塞所有读写操作

生产环境黄金法则

事务一定要短小精悍。长事务在 RR 级别下会长时间持有 Read View,导致对应的 Undo Log 版本无法被 Purge 线程清理,可能引发磁盘爆满和性能雪崩。

八、总结

核心要点关键描述
本质通过维护数据行的多个历史版本,实现读写互不阻塞的无锁并发控制
三大组件隐藏字段(DB_TRX_ID+DB_ROLL_PTR)+ Undo Log 版本链 + Read View
核心算法通过 Read View 的可见性规则,从版本链中找到当前事务能看到的版本
RC vs RRRC:每次 SELECT 新建 Read View;RR:事务内复用第一次的 Read View
快照读 vs 当前读快照读(普通 SELECT)不加锁读历史版本;当前读(FOR UPDATE/UPDATE)加锁读最新版
适用级别仅在 RC 和 RR 下生效

一句话记住 MVCC

MVCC 是 InnoDB 的“时光机”——通过 Undo Log 保存每个数据行的所有历史版本,再通过 Read View 为每个事务拍一张“快照”,让不同事务在同一时刻看到同一行数据的不同版本,从而实现读写互不阻塞的高性能并发控制。

面试高频考点

  1. MVCC 解决了什么问题?——读写冲突,让读不阻塞写、写不阻塞读
  2. RC 和 RR 在 MVCC 实现上有什么区别?——Read View 生成时机不同
  3. 快照读和当前读有什么区别?——快照读不加锁读历史版本,当前读加锁读最新版
  4. InnoDB 的 RR 级别如何解决幻读?——MVCC(复用 Read View)+ Next-Key Lock
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/12 10:32:46

终极指南:用search-plugins将qBittorrent打造成一站式种子搜索中心

终极指南&#xff1a;用search-plugins将qBittorrent打造成一站式种子搜索中心 【免费下载链接】search-plugins Search plugins for qBittorrent search feature 项目地址: https://gitcode.com/gh_mirrors/se/search-plugins 在数字时代&#xff0c;高效获取资源是每个…

作者头像 李华
网站建设 2026/8/12 10:31:54

Hadess与LDAP集成:企业统一身份认证实战指南

1. Hadess与LDAP集成概述Hadess作为一款新兴的开源身份认证管理系统&#xff0c;正在企业IT基础设施领域快速崛起。它最核心的价值在于能够无缝集成LDAP协议&#xff0c;解决困扰企业多年的多系统账号分散管理难题。我去年在一家300人规模的技术公司实施这套系统时&#xff0c;…

作者头像 李华
网站建设 2026/8/12 10:30:47

终极RPG Maker游戏资源解密工具:3步解锁加密文件的完整指南

终极RPG Maker游戏资源解密工具&#xff1a;3步解锁加密文件的完整指南 【免费下载链接】RPG-Maker-MV-Decrypter You can decrypt RPG-Maker-MV Resource Files with this project ~ If you dont wanna download it, you can use the Script on my HP: 项目地址: https://gi…

作者头像 李华
网站建设 2026/8/12 10:30:12

从零开始:使用FontMaker工具制作专属字库的完整指南

1. 项目概述&#xff1a;从零到一&#xff0c;打造你的专属字库 做设计或者开发的朋友&#xff0c;肯定都遇到过字体版权这个“老大难”问题。看中了一款特别有感觉的字体&#xff0c;一查授权协议&#xff0c;商业使用要么价格不菲&#xff0c;要么限制重重。自己动手画&#…

作者头像 李华
网站建设 2026/8/12 10:29:07

USB转串口驱动类型详解:VCP、CDC与原生驱动的原理、应用与故障排查

1. 项目概述&#xff1a;从一根线到一片海手里拿着一根USB转串口线&#xff0c;插上电脑&#xff0c;设备管理器里冒出个COM口&#xff0c;这事儿看起来简单得不能再简单了。但就是这个简单的动作背后&#xff0c;藏着一整个驱动类型的江湖。无论是玩单片机调试、搞工业设备通讯…

作者头像 李华