MVCC 原理

MVCC(Multi-Version Concurrency Control,多版本并发控制)是 MySQL 实现高并发的核心机制。

MVCC 基本概念

什么是 MVCC?

MVCC(Multi-Version Concurrency Control)是一种并发控制机制,通过维护数据的多个版本来实现:

  • 读不加锁

  • 写不阻塞读

  • 提高并发性能

为什么需要 MVCC?

传统方案(锁)的问题

-- 事务 A:读操作
BEGIN;
SELECT * FROM t WHERE id = 1;  -- 加读锁
-- 持有锁很长时间...

-- 事务 B:写操作
BEGIN;
UPDATE t SET a = 1 WHERE id = 1;  -- ⏳ 等待读锁释放
-- 读阻塞了写,并发性能差

MVCC 方案(快照)

MVCC 的核心思想

示意图

MVCC 只在哪些隔离级别下工作?

MVCC 在 READ COMMITTEDREPEATABLE READ 隔离级别下工作。

  • READ UNCOMMITTED:直接读最新数据,不需要 MVCC

  • SERIALIZABLE:完全串行化,不使用 MVCC


MVCC 的三大组件 ⭐⭐⭐⭐⭐

MVCC 由三个核心组件实现,这是面试必考的内容

1. 隐藏列(Hidden Columns)

InnoDB 为每行数据添加了三个隐藏列

列名
长度
作用

DB_TRX_ID

6 字节

最后修改该行的事务 ID

DB_ROLL_PTR

7 字节

回滚指针,指向 Undo Log 中的上一个版本

DB_ROW_ID

6 字节

隐藏的主键(如果表没有主键)

示例

关键点

  • DB_TRX_ID:标识是哪个事务修改的

  • DB_ROLL_PTR:指向上一个版本,形成版本链

2. Undo Log 版本链

每次修改数据时,旧版本会被保存到 Undo Log 中,通过 DB_ROLL_PTR 形成一个版本链

示例

版本链的形成

图示

关键点

  • 版本链是通过 DB_ROLL_PTR 连接的

  • 版本链保存在 Undo Log 中

  • 最新版本在表中,历史版本在 Undo Log 中

3. Read View(读视图)⭐⭐⭐⭐⭐

Read View 是一个数据结构,用于判断哪些版本对当前事务可见。

Read View 包含的信息

字段
含义

m_ids

当前活跃的事务 ID 列表

min_trx_id

m_ids 中的最小值

max_trx_id

系统应该分配给下一个事务的 ID(当前最大事务 ID + 1)

creator_trx_id

创建该 Read View 的事务 ID

示例

关键点

  • Read View 记录了创建快照时的活跃事务

  • 用于判断版本的可见性

  • RC 和 RR 级别创建 Read View 的时机不同(重要!)


Read View 详解

Read View 的创建时机

这是 RCRR 隔离级别的核心区别

READ COMMITTED(RC)

每次读取数据前都创建一个新的 Read View

结果

  • 每次都能读到最新已提交的数据

  • 导致不可重复读

REPEATABLE READ(RR)

只在第一次读取数据时创建 Read View,之后复用

结果

  • 始终读取同一个快照

  • 保证可重复读

Read View 的作用

用于判断版本链上的哪个版本对当前事务可见

判断流程


可见性判断算法 ⭐⭐⭐⭐⭐

这是 MVCC 的核心算法,面试经常要求手写详细描述

判断规则

对于版本链上的某个版本,其 DB_TRX_ID 记为 version_trx_id

根据以下规则判断该版本是否可见:

规则 1:当前事务自己修改的,可见

规则 2:版本在 Read View 创建前就已提交,可见

规则 3:版本是在 Read View 创建后才开始的,不可见

规则 4:版本的事务在 Read View 创建时是否活跃

完整算法(伪代码)

可见性判断示例

场景

判断 Charlie(TRX 300)

判断 Bob(TRX 200)

判断 Alice(TRX 100)

最终结果:读到 Alice


RC 和 RR 的 MVCC 区别 ⭐⭐⭐⭐⭐

这是面试的高频考点,必须理解两者的区别。

核心区别

隔离级别
Read View 创建时机
结果

READ COMMITTED

每次读取前都创建

能读到其他事务的最新提交

REPEATABLE READ

第一次读取时创建,之后复用

读取的是事务开始时的快照

详细示例

场景设置

READ COMMITTED 场景

REPEATABLE READ 场景

对比总结


MVCC 完整示例

示例 1:单事务修改,多事务读取

时间线

执行过程

示例 2:多个版本的查找

版本链

Read View

查找过程

如果 David 不可见:


MVCC 的局限性

1. 只对快照读有效 ⭐⭐⭐⭐⭐

MVCC 只对普通的 SELECT(快照读)有效,对当前读无效

2. 无法解决当前读的幻读

场景

解决方案Next-Key Lock(临键锁)

3. 写操作不使用 MVCC

总结


面试高频问题 ⭐⭐⭐⭐⭐

Q1: 什么是 MVCC?

回答

MVCC(Multi-Version Concurrency Control)是一种多版本并发控制机制,通过维护数据的多个版本来实现:

  1. 读不加锁:读取历史版本(快照),不需要加锁

  2. 写不阻塞读:写操作创建新版本,不影响读操作

  3. 提高并发:读写互不阻塞

核心思想

  • 不是让读等待写

  • 而是让读读取旧版本

  • 写创建新版本

Q2: MVCC 的实现原理?

回答

MVCC 由三大组件实现:

  1. 隐藏列

    • DB_TRX_ID:事务 ID

    • DB_ROLL_PTR:回滚指针

    • DB_ROW_ID:隐藏主键

  2. Undo Log 版本链

    • 每次修改保存旧版本

    • 通过 DB_ROLL_PTR 形成链表

    • 示例:Charlie → Bob → Alice

  3. Read View

    • 记录活跃事务列表

    • 判断版本可见性

    • RC 和 RR 创建时机不同

可见性判断

  • 自己修改的:可见

  • 已提交的旧版本:可见

  • 未来的事务:不可见

  • 活跃的事务:不可见

Q3: RC 和 RR 的 MVCC 有什么区别?

回答

核心区别在于 Read View 的创建时机

READ COMMITTED

  • 每次读取前都创建新的 Read View

  • 能读到其他事务的最新提交

  • 导致不可重复读

REPEATABLE READ

  • 只在第一次读取时创建 Read View

  • 之后的读取复用同一个 Read View

  • 保证可重复读

示例

Q4: MVCC 如何判断版本可见性?

回答

四条规则

查找流程

  1. 从最新版本开始

  2. 依次判断每个版本是否可见

  3. 返回第一个可见的版本

Q5: 快照读和当前读的区别?

快照读

  • 普通的 SELECT

  • 读取历史版本

  • 不加锁

  • 使用 MVCC

当前读

  • SELECT ... FOR UPDATE

  • INSERT、UPDATE、DELETE

  • 读取最新版本

  • 加锁

  • 不使用 MVCC

Q6: MVCC 能完全解决幻读吗?

回答

不能完全解决,需要配合 Next-Key Lock。

快照读的幻读:✅ MVCC 解决

当前读的幻读:❌ MVCC 无法解决,需要 Next-Key Lock

完整解决方案

Q7: 为什么 UPDATE 语句是当前读?

回答

UPDATE 必须基于最新数据修改,不能基于快照。

如果使用快照读(错误)

使用当前读(正确)

Q8: MVCC 的优缺点?

优点

  1. 读写不阻塞:提高并发性能

  2. 读不加锁:避免锁开销

  3. 实现快照隔离:保证一致性读

缺点

  1. 额外存储开销:需要维护多个版本

  2. 只对快照读有效:当前读仍需加锁

  3. 需要清理旧版本:Purge 操作有开销


总结

核心要点 ⭐⭐⭐⭐⭐

  1. MVCC 的三大组件

    • 隐藏列:DB_TRX_ID、DB_ROLL_PTR

    • Undo Log 版本链

    • Read View

  2. 可见性判断

    • 自己修改的:可见

    • 已提交的旧版本:可见

    • 未来的事务:不可见

    • 活跃的事务:不可见

  3. RC vs RR

    • RC:每次创建新 Read View

    • RR:第一次创建,后续复用

  4. MVCC 的局限

    • 只对快照读有效

    • 当前读需要加锁

    • 需要配合 Next-Key Lock 解决幻读

记住这些关键点

  • 三大组件

  • 四条可见性规则

  • RC 和 RR 的 Read View 创建时机

  • 快照读 vs 当前读

  • MVCC + Next-Key Lock 解决幻读

学习建议

  1. 画图理解

    • 画出版本链

    • 画出 Read View

    • 画出可见性判断过程

  2. 动手实验

    • 搭建 MySQL 环境

    • 模拟不同隔离级别

    • 验证可见性规则

  3. 结合锁机制

    • MVCC 解决读-写冲突

    • 锁解决写-写冲突

    • 两者配合实现隔离性


相关文档

Last updated