news 2026/5/7 7:25:01

MVCC机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MVCC机制

一、MVCC 到底是干嘛的?

MVCC 全称 Multi-Version Concurrency Control(多版本并发控制),核心作用就一个:让多个人同时读写同一条数据,不用互相锁着等,读写不冲突、不阻塞,大家都能顺畅操作。

举个最直白的例子:有人在修改一条数据(写操作),你同时去读这条数据(读操作),不用等他改完,你能正常读到数据,他也能正常修改,互不干扰——这就是MVCC的功劳。

二、核心比喻:用“图书阅览室”理解MVCC全貌

把MySQL数据库想象成一个「图书阅览室」,帮你快速理解MVCC的所有核心组件:

「书」= 数据库里的每一行数据;

「写操作」= 有人要修改书里的内容(比如把“1”改成“3”);

「读操作」= 有人要翻看这本书;

「undo log」= 阅览室的「档案柜」,专门存放“书的旧版本”(修改前先复印一份存档);

「Read View」= 你进阅览室时,管理员给你发的「阅读权限清单」(规定你能看哪些版本的书);

「事务」= 你在阅览室的「阅读/修改时长」(从进来到离开,期间你看到的书的版本是固定的)。

没有MVCC的情况:有人改书,就把阅览室锁起来,别人不能进、不能看,效率极低;

有MVCC的情况:改书的人改新书,看书的人读档案柜里的旧书,互不打扰,效率拉满。

三、MVCC 核心原理:3个关键组件

MVCC能实现“读写不冲突”,全靠3个核心组件协同工作,结合比喻一步步讲透,不跳任何步骤。

1. 组件1:每行数据的“隐藏3字段”

InnoDB引擎会在每一行数据背后,偷偷藏3个我们看不到的隐藏字段,这是MVCC的基础,相当于给每本书做了“版本标记”:

「DB_TRX_ID」:哪个事务最后修改了这行数据(相当于“这本书最后被谁修改过”,有唯一的事务ID);

「DB_ROLL_PTR」:版本指针,指向undo log里的“旧版本数据”(相当于“这本书的旧复印件,存放在档案柜的哪个位置”);

「DB_ROW_ID」:隐藏主键,没有手动设置主键时,MySQL会自动用这个字段当主键(无关核心逻辑,了解即可)。

2. 组件2:undo log(MVCC的“旧版本档案柜”)

之前讲三大日志时,我们知道undo log是“回滚日志”,但它还有一个更重要的作用——给MVCC提供“历史版本数据”,相当于阅览室的档案柜,专门存旧书复印件。

undo log的写入时机:执行增删改操作前,先把修改前的旧数据写入undo log(比如要把“1”改成“3”,先把“1”抄一份存进档案柜,再改书);

undo log的回收:事务提交后,不会立即删除,而是“延迟回收”(标记为“可回收”,等没有事务再需要这份旧版本时,后台自动清理,避免有人还在看旧书时,档案被删);

与MVCC的关联:undo log里的每一条旧数据,都是一行数据的“历史版本”,通过DB_ROLL_PTR指针,能串联起所有历史版本(比如从“3”找到“1”,再找到更早的版本)。

3. 组件3:Read View

当你开启一个事务,第一次执行查询时,MySQL会瞬间给你拍一张“快照”,这张快照就是Read View,相当于管理员给你的“阅读权限清单”——规定了你能看哪些版本的书,不能看哪些。

Read View里只有4个关键信息:

「m_ids」:当前正在阅览室里、还没离开(未提交)的人(事务)列表;

「min_trx_id」:这些未提交的人(事务)里,ID最小的那个;

「max_trx_id」:下一个要进入阅览室的人(事务)的ID(相当于“下一个读者的编号”);

「creator_trx_id」:你自己的事务ID(相当于“你的读者编号”)。

核心作用:Read View决定了你能看到哪一个版本的数据——不是最新的,而是“你有权限看的、最适合的版本”。

四、MVCC 核心逻辑:怎么判断“我能读哪个版本”?

当你查询某一行数据时,MySQL会拿着这行数据的「DB_TRX_ID」(最后修改它的事务ID),对照你的Read View(阅读权限清单),按4条简单规则判断,全程不用复杂计算:

规则1:自己改的,能看

如果这行数据的DB_TRX_ID(最后修改者),和你的creator_trx_id(你自己的事务ID)一样 → 这是你自己改的,当然能看(自己改的书,自己肯定能看)。

规则2:早于你进入的、已提交的,能看

如果这行数据的DB_TRX_ID,比Read View里的min_trx_id(当前未提交事务的最小ID)还小 → 说明修改这行数据的人,在你进入阅览室之前就已经离开(提交事务)了,这是“历史稳定版本”,能看(别人早就改完走了,你看他改后的版本没问题)。

规则3:晚于你进入的、未提交的,不能看

如果这行数据的DB_TRX_ID,大于或等于Read View里的max_trx_id(下一个事务ID) → 说明修改这行数据的人,是在你之后进入阅览室的,而且还没离开(未提交),你不能看(别人还在改,你不能看半成品)。

规则4:和你同时在、未提交的,不能看

如果这行数据的DB_TRX_ID,在min_trx_id和max_trx_id之间,而且在m_ids(当前未提交事务列表)里 → 说明修改这行数据的人,和你同时在阅览室(未提交),你不能看(别人还没改完,你看不到他的修改)。

关键补充:读不到怎么办?

如果按上面的规则,判断当前版本不能看,MySQL会顺着这行数据的「DB_ROLL_PTR」指针,去undo log(档案柜)里找“上一个旧版本”,再用上面的规则重新判断,直到找到一个“你能看的版本”为止。

五、经典场景演示

用“读旧数据、数据覆盖别人修改的数据”场景,一步步演示MVCC的工作过程,看完就懂为什么会读旧数据,以及为什么会覆盖别人修改后的数据。

场景:初始数据 num=1,两个事务同时操作

1.时刻1:你开启事务A(creator_trx_id=100),执行查询:SELECT num FROM t WHERE id=1;

MySQL给你生成Read View:m_ids=[], min_trx_id=101, max_trx_id=101, creator_trx_id=100;

判断:当前数据num=1的DB_TRX_ID=0(初始无修改),小于min_trx_id=101,能看;

你读到:num=1(旧版本)。

2.时刻2:别人开启事务B(creator_trx_id=101),执行修改:UPDATE t SET num=3 WHERE id=1;

先写undo log:把num=1的旧数据存入undo log,DB_ROLL_PTR指向这条旧数据;

内存中把num改成3,数据行的DB_TRX_ID更新为101;

事务B提交(相当于“改书的人离开阅览室”)。

3.时刻3:你在事务A中,再次执行查询:SELECT num FROM t WHERE id=1;

你依然用“时刻1生成的Read View”(MySQL默认隔离级别下,事务内Read View全程不变);

判断:当前数据num=3的DB_TRX_ID=101,等于max_trx_id=101,不能看;

顺着DB_ROLL_PTR去undo log找旧版本,找到num=1(DB_TRX_ID=0),能看;

你读到的还是:num=1(旧数据)——这就是“读不到最新数据”的原因。

4.时刻4:你在事务A中,执行修改:UPDATE t SET num=1+1 WHERE id=1;

你基于读到的旧数据1,计算出2,执行更新;

此时,事务B已经提交,数据实际是3,但你看不到;

更新后,数据被你改成2,覆盖了别人的3——这就是“数据覆盖”的问题。

六、核心疑问解答

疑问1:读不到最新数据,岂不是数据有误?

结论:不算错误,这是MySQL“事务隔离”的正常行为,是故意设计的。

解释:MVCC的核心目的是“保证同一个事务内,看到的数据一致、不混乱”,而不是“保证读到最新数据”。就像你手里拿着一份9点的报纸(Read View),不管之后出了多少晚报(别人的修改),你手里的报纸始终是9点的——宁可读旧数据,也不能让你在同一个事务里,前后读到不一样的数据(比如第一次读1,第二次读3,算错账)。

补充:如果需要读到最新数据,不要用长事务,读完就提交,每次查询都会生成新的Read View,就能看到别人提交的最新修改。

疑问2:(重点)读旧数据后修改,会覆盖别人的新数据,怎么避免?

结论:MVCC只保证“读安全、读一致”,不保证“写安全”,单纯靠MVCC先读后改,一定会出现覆盖,需要额外加机制。

3种小白也能上手的解决方案(真实业务常用):

1.方案1:SELECT ... FOR UPDATE(加行锁,最常用)
读数据时,直接给数据上锁:SELECT num FROM t WHERE id=1 FOR UPDATE; ,别人要改这条数据,必须等你事务结束,你读的一定是最新数据,不会基于旧值修改,避免覆盖。

2.方案2:原子更新SQL(不用先读后改)
不要先读num=1,再计算2,直接让MySQL自己计算:UPDATE t SET num = num + 1 WHERE id=1; ,这是原子操作,不会被打断,MySQL会自动处理并发,绝对安全。

3.方案3:乐观锁(版本号机制)
给表加一个version字段(版本号),更新时判断版本是否一致:UPDATE t SET num=2, version=version+1 WHERE id=1 AND version=旧版本号; ,如果别人改了数据,版本号会变,更新失败,你重试即可。

疑问3:MVCC和undo log的关联,到底是什么?

结论:undo log是MVCC的“历史版本仓库”,没有undo log,MVCC就无法提供旧版本数据,也就无法实现“读写不冲突”。

简单说:MVCC负责“判断你能看哪个版本”,undo log负责“提供这个版本的数据”,两者缺一不可——没有undo log,MySQL找不到旧版本,只能给你上锁,就失去了MVCC的意义。

七、MVCC 与事务隔离级别

MVCC主要支持MySQL的2种事务隔离级别,行为不同,对应不同的业务场景,结合Read View的生成时机就能看懂:

1. REPEATABLE READ(可重复读,MySQL默认)

核心特点:事务开始时,生成一次Read View,全程用到底。

效果:不管别人怎么修改、怎么提交,你在同一个事务里,读到的始终是事务开始时的旧版本(就像手里的报纸全程不变),实现“可重复读”,避免“不可重复读”,但会一直读旧数据。

2. READ COMMITTED(读已提交)

核心特点:每次执行SELECT时,都会生成新的Read View。

效果:别人提交了新修改,你下次查询就能看到最新数据,不会一直读旧数据,但可能出现“不可重复读”(同一个事务里,两次查询结果不一样)。

3. SERIALIZABLE(串行化)

不使用MVCC,直接给数据上锁,读写排队执行,完全避免并发问题,但性能极低,适合对数据一致性要求极高、并发量极低的场景(如银行对账)。

八、极简总结

1.MVCC = 多版本并发控制,核心是“读写不冲突、不阻塞”;

2.3个核心组件:数据隐藏字段(版本标记)、undo log(旧版本仓库)、Read View(阅读权限);

3.读旧数据不是错误,是事务隔离的正常行为,保证事务内数据一致;

4.MVCC不保证写安全,先读后改会覆盖别人数据,需用加锁、原子更新、乐观锁解决;

5.undo log不仅用于回滚,更是MVCC的核心,没有undo log就没有MVCC。

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

AI文档提取利器:extract-llms-docs助力RAG知识库自动化构建

1. 项目概述:一个为AI开发者量身定制的文档提取利器如果你是一名AI应用开发者,或者正在构建基于大语言模型的智能体,那么你一定遇到过这样的困境:为了给你的AI Agent“喂”知识,你需要从海量的官方文档、技术博客、API…

作者头像 李华
网站建设 2026/5/7 7:22:39

vue基于springboot的房屋租赁续租系统的设计与实现

目录同行可拿货,招校园代理 ,本人源头供货商功能模块划分续租业务流程系统支撑功能技术实现要点扩展性设计项目技术支持源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作同行可拿货,招校园代理 ,本人源头供货商 功能模块划分 用户管理模块 …

作者头像 李华
网站建设 2026/5/7 7:21:14

机器人轨迹数据采集:从多传感器同步到高效存储的工程实践

1. 项目概述:一个为机器人轨迹数据收集而生的工具最近在折腾机器人相关的项目,特别是涉及到强化学习或者模仿学习的时候,最头疼的就是数据从哪里来。仿真环境的数据虽然好获取,但和真实世界总有差距;而直接上真机采集&…

作者头像 李华
网站建设 2026/5/7 7:15:41

基于Tauri与React构建跨平台AI技能管理器:实现技能一键共享与同步

1. 项目概述:一个桌面端的AI技能管理器如果你和我一样,深度使用Cursor、Claude Code、OpenClaw、OpenCode这类AI编程助手,那你一定遇到过“技能管理”的痛点。每个项目、每个Agent(比如Cursor的Agent模式、Claude Code的Workflow&…

作者头像 李华
网站建设 2026/5/7 7:14:51

2026免费图片去水印软件怎么选?手机/电脑免费去水印工具实测对比

水印几乎出现在互联网上所有热门的图片和视频中。无论你是想保存喜欢的内容、制作素材库,还是需要处理工作中的图片,去水印都成了日常的需求。问题是,去水印工具千千万,哪些真的免费?哪些效果好用?2026年到…

作者头像 李华
网站建设 2026/5/7 7:11:31

macOS光标卡顿修复:基于NSCursor与CGEvent的系统级解决方案

1. 项目概述:解决macOS光标卡顿的终极方案如果你是一名macOS的深度用户,尤其是像我这样经常在多个显示器、虚拟机窗口和复杂应用之间切换的开发者或设计师,那么你大概率遇到过那个令人抓狂的问题:鼠标光标“卡住”了。具体来说&am…

作者头像 李华