MVCC
MySQL InnoDB 的事务隔离是通过「锁 + MVCC(多版本并发控制)」共同实现的。
写操作:主要靠 锁
读操作:主要靠 MVCC
MVCC 的核心思想:
通过维护数据的多个版本,使读写不冲突,从而提高并发性能。
一、MVCC 的三个核心组件
这里是 100%面试必背结构:
MVCC 依赖三个东西:
├─ 隐藏字段
├─ undo log
└─ ReadView
我们一个一个解释。
二、隐藏字段(Hidden Columns)
InnoDB 每一行数据 都会自动加两个隐藏字段。
row
├─ 数据列
├─ trx_id
└─ roll_pointer
1 trx_id(事务ID)
trx_id
含义:
最后一次修改这行数据的事务ID
举例:
事务1 更新了 rowA
trx_id = 1
事务2 又更新 rowA
trx_id = 2
说明:
当前版本是事务2产生的。
2 roll_pointer(回滚指针)
roll_pointer
作用:
指向 undo log 里的旧版本
比如:
row 最新版本
↓
undo log version1
↓
undo log version0
于是形成一个:
版本链
version3 -> version2 -> version1 -> version0
这个链叫:
Version Chain(版本链)
三、undo log(回滚日志)
这个东西非常关键。
undo log 主要有两个作用:
1 支持事务回滚
如果事务失败:
rollback
就通过 undo log 恢复数据。
2 存储历史版本(MVCC核心)
每次 update:
old value -> undo log
所以数据形成:
最新数据
↓
旧版本
↓
更旧版本
例如:
余额 = 100
事务A -> 改成 120
事务B -> 改成 150
版本链:
150 (trx_id=3)
↓
120 (trx_id=2)
↓
100 (trx_id=1)
四、ReadView(读视图)
这个才是 MVCC最核心最难的地方。
ReadView 的作用是:
决定当前事务能看到哪个版本。
换句话说:
在版本链里选一个版本给你读。
ReadView里最重要的信息
ReadView里面记录:
m_ids 当前活跃事务id列表
min_trx_id 最小活跃事务
max_trx_id 下一个事务id
creator_trx_id 创建ReadView的事务
但是你面试只要记住 三个规则。
五、版本可见性判断(面试重点)
当你读一行数据时:
MySQL 会:
沿着版本链往下找
直到找到 对当前事务可见的版本。
判断规则:
情况1
trx_id < min_trx_id
说明:
这个事务 早就提交了
可以读。
情况2
trx_id > max_trx_id
说明:
这个版本是 未来事务产生的
不能读。
情况3
min_trx_id <= trx_id <= max_trx_id
如果:
trx_id ∈ m_ids
说明:
事务 还没提交
不能读。
否则:
可以读。
六、完整流程(真正理解MVCC)
我们用一个真实例子走一遍。
假设:
事务A 开始
事务B 修改数据
事务C 查询数据
原始数据:
balance = 100
trx_id = 1
Step1
事务B:
update balance = 200
trx_id = 2
版本链:
200 (trx_id=2)
↓
100 (trx_id=1)
Step2
事务C查询:
此时生成:
ReadView
如果:
事务2还没提交。
ReadView看到:
trx_id = 2 是活跃事务
于是:
不能读。
于是:
往下找版本。
100 (trx_id=1)
这个版本:
已提交。
所以:
事务C看到的是100。
七、为什么MVCC可以提高性能
这个点面试官特别爱问。
传统锁模型:
读 -> 加锁
写 -> 加锁
读写冲突:
读被写阻塞
写被读阻塞
MVCC:
读 -> 不加锁
写 -> 生成新版本
所以:
读写不冲突
这叫:
Snapshot Read(快照读)
八、快照读 vs 当前读(面试必问)
快照读(MVCC)
例如:
select * from user;
读取:
历史版本
不加锁。
当前读(加锁)
例如:
select … for update
select … lock in share mode
update
delete
insert
读取:
最新版本
并加锁。
九、不同隔离级别下 ReadView 不同
这是 面试超高频细节。
RC(Read Committed)
每次查询:
生成新的 ReadView
所以:
可以看到最新提交数据。
RR(Repeatable Read)
事务第一次查询:
生成 ReadView
之后:
一直复用
所以:
事务里数据 不会变化。
这就是:
可重复读。