news 2026/9/23 9:34:33

MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

之前写过事务隔离级别——脏读、不可重复读、幻读。但那是表象。

隔离级别是怎么实现的?为什么InnoDB能做到“读不阻塞写、写不阻塞读”?为什么RR级别下幻读没有被完全解决?

答案都在MVCC(多版本并发控制)里。

今天把MVCC的底层机制彻底拆开讲清楚。

一、什么是MVCC

MVCC(Multi-Version Concurrency Control),多版本并发控制。它的核心思想是:同一行数据在数据库中保留多个版本,不同事务读取时看到的是符合自己隔离级别的那个版本。

InnoDB为每一行数据维护了三个隐藏字段:

  • DB_TRX_ID(6字节):最后一次修改该行的事务ID

  • DB_ROLL_PTR(7字节):指向Undo Log中该行的上一个版本

  • DB_ROW_ID(6字节):如果没有显式主键,InnoDB用它生成聚簇索引

DB_ROLL_PTR是版本链的关键——通过它,当前行可以一路回溯到所有历史版本。

二、版本链是怎么构建的

每次对数据进行修改(UPDATE或DELETE),InnoDB都会在Undo Log中记录修改前的旧值。修改后,当前行的DB_ROLL_PTR指向Undo Log中的旧版本。

举个例子:一个事务把id=1的name从"张三"改成"李四",再把age从25改成26。版本链会变成:

当前行: name=李四, age=26, trx_id=100, roll_ptr -> Undo Log ↑ Undo Log: name=李四, age=25, trx_id=100, roll_ptr -> Undo Log ↑ Undo Log: name=张三, age=25, trx_id=90, roll_ptr -> NULL

版本链的每一个版本都记录了修改它的事务ID。这个ID是Read View判断“哪个版本可见”的核心依据。

三、Read View:决定哪个版本可见

Read View是事务在快照读时生成的一个“可见性判断规则”。它包含四个核心字段:

字段含义
m_ids当前活跃事务ID列表(已启动但未提交的事务)
min_trx_id活跃事务中最小的事务ID
max_trx_id下一个将要分配的事务ID
creator_trx_id创建这个Read View的事务ID

当一个事务要读取某行数据时,沿着版本链从头开始找:

  1. 如果版本的trx_id小于min_trx_id,说明这个版本在Read View创建之前就已提交——可见

  2. 如果版本的trx_id大于等于max_trx_id,说明这个版本在Read View创建之后才启动——不可见

  3. 如果trx_idmin_trx_idmax_trx_id之间:如果trx_idm_ids列表中,说明事务还活跃——不可见;如果不在,说明已提交——可见

沿着版本链一直找到第一个可见的版本,就是当前事务能读到的数据。

四、RC和RR的Read View差异

这是MVCC最核心的差异点。

隔离级别Read View生成时机效果
READ COMMITTED每次SELECT都生成新的Read View能读到其他事务最新提交的数据
REPEATABLE READ只在事务第一次SELECT时生成,之后复用整个事务期间看到的是同一份快照

RC下,事务A第一次SELECT时看到的是当时的数据快照。事务B提交后,事务A再次SELECT,会生成新的Read View,看到事务B提交的数据。这就是“读已提交”。

RR下,事务A第一次SELECT生成Read View后,后续所有SELECT都复用这个Read View。即使事务B提交了,事务A也看不到——因为它仍然用旧Read View判断可见性。这就是“可重复读”。

这也解释了为什么RR级别下幻读没有被完全解决。快照读(普通SELECT)下,RR确实避免了幻读——因为Read View不变,新插入的行对当前事务不可见。但当前读SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE)会读取最新数据,如果另一个事务插入了新行,当前读会看到它——这就是幻读。

五、快照读与当前读

InnoDB的读操作分两种:

快照读(Snapshot Read):普通的SELECT语句。读取的是Read View决定的历史版本,不加锁,不阻塞写。

当前读(Current Read)SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODEINSERTUPDATEDELETE。读取的是最新版本,需要加锁。

读类型语句是否加锁读取版本
快照读普通SELECT不加锁Read View决定的版本
当前读FOR UPDATE / 写操作加锁最新版本

MVCC的核心价值就在于快照读——读操作不需要加锁,写操作不会被读阻塞。这是InnoDB高并发性能的基石。

六、长事务与Undo Log膨胀

MVCC有一个代价:历史版本必须保留到不再被任何Read View需要为止。

如果一个事务长时间不提交,它持有的Read View会一直阻止InnoDB清理旧版本。Undo Log不断膨胀,占用大量表空间。

监控长事务

SELECT trx_id, trx_state, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec FROM information_schema.innodb_trx WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60;

如果发现长事务,需要评估是否可以提交或回滚。监控指标Innodb_history_list_length持续增长,说明Undo Log清理不及时,版本链过长。

七、小结

MVCC是InnoDB并发控制的内核。版本链记录历史,Read View决定可见性,快照读利用MVCC实现无锁读,当前读读取最新版本并加锁。RC和RR的核心差异在于Read View的生成时机——每次SELECT vs 首次SELECT。理解MVCC,才能理解InnoDB为什么能做到高并发下的读写不互相阻塞。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

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

小木屋免费手机影院新手避坑指南:3个致命错误让你少走弯路

小木屋免费手机影院新手避坑指南:3个致命错误让你少走弯路 别再看那几百页的官方文档了,真的,直接看这篇。 刚入行或者转行做开发的朋友,是不是经常被官方文档劝退?密密麻麻的文字,术语满天飞,看完还是不知道代码该往哪写。这就是典型的“新手避坑”场景。很多人以为技术难点在算法,其实90%的坑都出在基础配置…

作者头像 李华
网站建设 2026/9/23 9:34:11

面试必问叶子结点:3个代码案例搞懂底层逻辑

面试必问叶子结点:3个代码案例搞懂底层逻辑 刚毕业找工作的同学,是不是经常陷入一个死循环:教程刷了几十个,LeetCode 做了几百道,但一到面试或者接手真实项目,脑子就一片空白?特别是遇到【叶子结点】这种看似基础,实则坑点极多的概念时,面试官随口一问,你要么支支吾吾,要么写出来的代码全是…

作者头像 李华
网站建设 2026/9/23 9:34:06

搞定公司部门分类逻辑,从入门到精通的实战源码拆解

搞定公司部门分类逻辑,从入门到精通的实战源码拆解 看了一堆教程还是不会写项目?这是很多开发者在接手企业级后台系统时最真实的写照。理论都懂,一到处理“公司部门分类”这种看似简单实则复杂的层级数据,代码就写得一团糟。想从入门到精通,光背API没用,必须看透底层逻辑。今天咱们不聊虚的,直接拆解一个经典的企…

作者头像 李华
网站建设 2026/9/23 9:33:58

5年踩坑总结:厚积薄发的例子保姆级教程,API变更不再慌

5年踩坑总结:厚积薄发的例子保姆级教程,API变更不再慌 版本升级后 API 全变了,代码直接报错,这种崩溃感谁懂?别急着骂娘,这正是检验你技术底子的时刻。这份厚积薄发的例子保姆级教程,专为被框架迭代折磨过的开发者准备。我们不讲虚的,直接拆解如何在动荡的技术环境中,通过积累底层逻辑来应对上层…

作者头像 李华
网站建设 2026/9/23 9:33:50

3个致命坑:CustomValidator面试避坑指南

3个致命坑:CustomValidator面试避坑指南 面试官盯着屏幕问:“说说 CustomValidator 底层原理,为什么不用 JS 校验?” 你心里一紧,答非所问,场面瞬间尴尬。 别慌,这份避坑指南带你拆解核心逻辑,面试不再卡壳。 考点梳理:面试官到底在考什么 很多开发者把…

作者头像 李华