MVCC 如何工作?

未标记

询问 AI

考查重点:多版本可见性与并发控制。数据行保留多个版本,事务按快照可见性读取适合的版本;写入仍需冲突控制,旧版本待无人需要后回收

本题目录

参考答案

核心回答

MVCC(多版本并发控制)让不同事务根据自己的可见性规则,读取同一行的不同版本,减少普通查询与写操作之间的阻塞。以 MySQL 8.4 InnoDB 为例,关键是记录中的事务信息、undo 历史版本,以及决定哪些版本可见的 Read View。

事务修改记录时,InnoDB 更新当前记录,并保存撤销和构造旧版本所需的 undo 信息。一致性读先检查当前版本是否对自己的 Read View 可见;不可见,就沿回滚指针利用 undo 构造更早版本,直到找到可见版本,或确认该行在这个视图中不存在。Read View 是可见性信息,不是给整个数据库复制一份数据。

READ COMMITTED 下,每次一致性读建立新视图;REPEATABLE READ 下,通常由第一次一致性读建立视图,之后沿用,同时可以看到本事务自己的修改。MVCC 主要帮助非锁定的一致性读;写操作和 SELECT ... FOR UPDATE 等锁定读仍需要相应的锁,不能把它理解成数据库完全无锁。

三个组成部分怎样配合

记录的事务信息告诉系统“这个版本由谁产生”。聚簇索引记录中的 DB_TRX_ID 标识最近插入或更新它的事务,DB_ROLL_PTR 指向相关 undo 记录;删除也会先表现为相应的删除标记,而不是立即抹掉所有历史。

undo提供恢复旧内容所需的信息。它既服务于事务回滚,也可以供一致性读构造旧版本。这里的“版本链”是便于理解的说法,并不表示每次更新都在表里复制一整行,也不要求每条 undo 都保存完整旧行。

Read View记录创建视图时的事务可见性边界,例如哪些已分配 ID 的读写事务仍活跃、下一待分配事务 ID,以及当前事务自己的身份。系统据此判断一条记录版本能否被这次读取使用,而不是只比较记录的更新时间。

Read View 怎样判断可见性

可以把判断简化为四种情况,先单独处理当前事务自己的版本:

  1. 本事务产生的版本可见。 即使不是视图创建前就存在,也能读到自己已经执行的修改。
  2. 版本事务 ID 小于低水位 L,可见。 产生该版本的事务已在视图建立前完成提交。
  3. 版本事务 ID 大于等于高水位 U,不可见。 这个 ID 在建立视图时尚未分配。
  4. ID 位于 L 与 U 之间,看活跃事务集合。 在集合中,说明当时尚未提交,对该视图不可见;不在集合中,则对该视图可见。

L 是视图中其他活跃读写事务 ID 的最小值;集合为空时可取 U。U 是当时下一待分配的事务 ID,不是活跃集合中的最大 ID。MySQL 源码中 m_up_limit_id 对应这里的 L,m_low_limit_id 对应 U,名称容易和数值大小混淆。

遇到不可见版本,继续用 undo 寻找更早版本。事务 ID 反映分配顺序,不等于提交顺序;不能简单断言“比我的事务 ID 小的修改全部可见”。

一个并发修改的例子

假设余额最早是 100,由已提交事务 99 写入。事务 B(ID 103)将余额改为 80,但尚未提交。事务 A 进行一致性读时,Read View 记录 B 仍然活跃。

当前的 80 属于事务 103,对 A 不可见;沿 undo 构造出事务 99 写入的 100,A 就读到 100。B 随后提交,情况取决于 A 的隔离级别和下一次读取方式:

  • A 在 REPEATABLE READ 下继续普通一致性读,仍使用原视图,B 在该视图里仍不可见,所以读到 100。
  • A 在 READ COMMITTED 下再次一致性读,新视图已不把 B 视为活跃事务,可以读到 80。
  • A 自己随后修改这条记录,则可以读到自己的修改;不能把“同一视图”理解成自己的写入也永远看不到。

这些结论讨论的是一致性读,不直接套用于锁定读和更新语句。普通 BEGIN 本身通常不建立用于后续读取的快照;在适用的 REPEATABLE READ 隔离级别下,START TRANSACTION WITH CONSISTENT SNAPSHOT 可以在启动时建立一致性视图。

常见追问

MVCC 能解决所有并发问题吗?

不能。非锁定读取可以绕过其他事务的行修改锁去读旧版本,但写写冲突、锁定读和业务约束仍需处理。对范围内新增记录的限制,也要结合隔离级别、实际语句、索引与范围锁分析,不能仅凭“开了 MVCC”就断言所有操作都看同一份数据。

普通 SELECT 完全不需要锁吗?

在这里讨论的 RC/RR 一致性读中,不为读取记录设置锁定读那样的行锁;数据库内部仍有元数据锁、短时同步等机制。带锁定子句或包含在特殊语句中的读取,也要按相应规则判断。

为什么长事务可能让 undo 越来越多?

旧视图可能仍需要历史信息,即使产生修改的事务已经提交,相关 update undo 也不能立即清理。旧版本保留时间变长,会增加存储和维护压力;应结合活跃事务、历史链和清理情况排查,而不是直接删除仍有读取者需要的历史。

同一事务混用快照读和锁定读,会怎样?

两者可能针对不同时间边界的数据。锁定读和更新操作要处理当前可操作的记录及锁冲突,而普通一致性读沿用自己的视图。业务若依赖“检查后再修改”的关系,应使用适当的事务、锁或条件更新,让约束在真正写入时仍成立。

参考资料

相关面经

11 篇

蚂蚁集团

面经合集
查看原面经 →
MySQL 的 MVCC 基于什么机制实现的?它能解决幻读吗?如果能,具体怎么做到的?

蚂蚁集团

面经合集
查看原面经 →
MVCC

百度

面经合集
查看原面经 →
讲一下db里面的MVCC

百度

Agent 应用开发工程师
秋招一面
查看原面经 →
MVCC 是怎么实现的?

科大讯飞

Java 开发工程师
秋招一面、二面
查看原面经 →
MVCC是什么,能详细讲一讲吗
展开剩余 6 篇面经

美团

后端开发工程师(AI)
一面
查看原面经 →
讲讲MVCC;

百度

Agent 应用开发工程师
秋招一面、二面、三面
查看原面经 →
讲一下db里面的MVCC

滴滴

国际化后端开发
社招一面、二面、三面
查看原面经 →
介绍下 MVCC 的原理,MVCC 有什么优缺点?

滴滴

AI Agent 后端开发
查看原面经 →
`MVCC` 是怎么实现的?

深势科技

后端开发工程师(AI / Agent 方向)
二面
查看原面经 →
MVCC 的定义是怎样的?

腾讯音乐

后端开发工程师
实习一面
查看原面经 →
你了解 MySQL 的 MVCC吗?能够简单说一下吗?
那 MVCC 这一块是如何帮助我们提升数据库并发的读写性能呢?还有它的锁机制主要是为了解决哪些问题?

收录题集

2 份

相关题目

4 题