大数跨境
0
0

MYSQL  MVCC 多版本并发控制的原理?

MYSQL  MVCC 多版本并发控制的原理? Linux运维技术之路
2025-12-04
8
导读:MYSQL MVCC 多版本并发控制的原理?在学习 InnoDB 并发控制之前,MVCC 是绕不过去的核心机制。

 










 

MYSQL  MVCC 多版本并发控制的原理?

在学习 InnoDB 并发控制之前,MVCC 是绕不过去的核心机制。
它既能实现“高并发读”,又能避免“加大量锁”。

  • • MVCC 是如何实现快照读的?
  • • undo log、隐藏列、Read View 各自担什么责任?
  • • 为什么 RR 能避免幻读而 RC 不能?
  • • MVCC 和加锁有什么关系?

一、什么是 MVCC?一句话总结

MVCC(Multiversion Concurrency Control)多版本并发控制,是 InnoDB 为了提高读写并发而设计的机制。

核心思想:

写不阻塞读,读不阻塞写。
通过维护数据的多个版本,让读操作读取“过去的版本”,而不是最新版本。

它主要用于 快照读(无需加锁的读),例如:


   
    
   SELECT * FROM user WHERE id = 1;

而非:


   
    
   SELECT * FROM user WHERE id = 1 FOR UPDATE;

后者是当前读,需要加锁,不走 MVCC。


二、MVCC 的三大核心组件(决定你是否读到旧版本)

MVCC 依赖三样东西:

1)undo log(历史版本链)

每次 UPDATE、DELETE 时:

  • • 新值写到数据行
  • • 旧值写入 undo log
  • • 并由 roll_pointer 指向旧版本

形成一条链:


   
    
   最新版本 → 上一版本 → 上上一版本 → ...

这是 MVCC 的“历史仓库”。


2)两列隐藏字段(trx_id、roll_pointer)

InnoDB 自动为每行维护两个隐藏列:

字段
含义
trx_id
写入该行的事务 ID
roll_pointer
指向 undo log 的指针

这两个字段是 MVCC 的基础。


3)Read View(可见性规则)

每个事务在执行快照读时,会生成一个 Read View,里面记录:

  • • 当前活跃未提交事务的 ID 列表(m_ids)
  • • 最小未提交事务 ID(low_limit_id)
  • • 最大已分配事务 ID(up_limit_id)

通过 Read View 判断一行数据是否对当前事务可见。

核心规则:

如果一个版本的 trx_id 在“已提交事务范围”内,则可见;否则不可见,需要沿着 undo log 继续往前找历史版本。

这是 MVCC 的灵魂。


三、MVCC 是如何实现“快照读”的?(核心原理)

假设表中某行有版本链:


   
    
   v3 (trx_id=30)

v2 (trx_id=20)

v1 (trx_id=10)

事务 A(trx_id = 25)做快照读:

InnoDB 会:

  1. 1. 读取最新版本(trx_id = 30)
  2. 2. 判断 trx_id=30 是否对事务 A 可见
    → 不可见(因为 trx_id 30 > 25)
  3. 3. 读取 undo log → 版本 v2(trx_id = 20)
  4. 4. 判断 trx_id=20 是否可见
    → 可见
  5. 5. 返回 v2

最终:

A 并不会看到最新提交的 v3,而是看到 v2(它开始时的世界)。

这就是“多版本”的意义。


四、MVCC 在不同隔离级别下的行为

MVCC 只在 RC(读已提交) 和 RR(可重复读) 下工作。

1)RC:每次 SELECT 都创建新的 Read View

效果:

  • • 每次都看到最新已提交版本
  • • 可能出现不可重复读

2)RR:同一个事务内只创建一次 Read View

效果:

  • • 每次 SELECT 的快照相同
  • • 能避免不可重复读
  • • 幻读通过 Next-Key Lock 解决,不是 MVCC

👉 RR 并不是靠 MVCC 避免幻读,而是靠锁。


五、MVCC 与锁的关系(非常容易考)

很多人混淆:

  • • MVCC 用于 快照读(普通 SELECT)
  • • 锁用于 当前读(UPDATE / DELETE / FOR UPDATE)

对比:

操作
是否加锁
是否使用 MVCC
SELECT
❌ 不加锁
✔ 使用 MVCC
SELECT … FOR UPDATE
✔ 加锁
❌ 不走 MVCC
UPDATE
✔ 加锁
❌ 不走 MVCC
DELETE
✔ 加锁
❌ 不走 MVCC

所以:

MVCC 并不能解决所有并发问题,写操作仍然需要锁来保护。


六、MVCC 的实际意义(为什么这么优秀?)

1)高并发读

读操作不加锁 → 不阻塞写操作。

2)提升 OLTP 系统吞吐

普通 SELECT 基本不冲突,支持大量 QPS。

3)事务隔离简洁高效

RR 通过固定快照 + 锁实现一致性。

4)历史版本可以用于回滚

undo log 不仅用于 MVCC,还用于:

  • • 回滚事务
  • • 崩溃恢复

一举三得。


七、一个最经典的 MVCC 示例(理解后彻底通透)

场景

事务 A(trx_id = 100):


   
    
   SELECT * FROM user WHERE id = 1;

事务 B(trx_id = 105):


   
    
   UPDATE user SET age = 20 WHERE id = 1;
COMMIT
;

事务 A 再查:


   
    
   SELECT * FROM user WHERE id = 1;

行版本链:


   
    
   v2 (trx_id=105)  age=20

v1 (trx_id=90)   age=18

事务 A 的 Read View 看到:

  • • 90 < 100 → 已提交 → 可见
  • • 105 > 100 → 不可见

所以两次 SELECT 都读到:


   
    
   age=18

即:可重复读实现了!


八、总结

InnoDB 的 MVCC 是通过 undo log 维护的多版本链、行的隐藏字段(trx_id、roll_pointer)以及 Read View 可见性规则实现的。
快照读通过 Read View 判断应该读取哪个版本,实现“读写不阻塞”。
RC 每次读都会生成新的 Read View;RR 同一个事务只生成一次。
MVCC 只用于普通 SELECT,当前读(UPDATE/DELETE/SELECT FOR UPDATE)必须加锁,不走 MVCC。

 



 

 


往期回顾

【声明】内容源于网络
0
0
Linux运维技术之路
专注运维架构、高可用、高并发、高性能、大数据、容器化、数据库、python、devops等开源技术和实践的分享。
内容 347
粉丝 0
Linux运维技术之路 专注运维架构、高可用、高并发、高性能、大数据、容器化、数据库、python、devops等开源技术和实践的分享。
总阅读760
粉丝0
内容347