多版本并发控制(Multi-Version Concurrency Control, MVCC)实现提交读和可重复读这两种隔离级别。未提交读无需使用 MVCC。可串行化单纯使用 MVCC 无法实现。
1 原理
MVCC 是行级锁的一个变种,尽可能避免加锁,非阻塞读操作,写操作只锁定必要的行。
MVCC 是通过保存数据在某个时间点的快照来实现的。任意时间每个事务看到的数据一致。不同事务同一时刻看到的数据可能不同。
1.1 版本号
- 系统版本号:递增的数字,每个新事务自动递增。
- 事务版本号:事务开始时的系统版本号。
1.2 隐藏列
MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号:
- 行版本号:该快照创建时间的
系统版本号
; - 行删除标识:如删除版本号大于当前
事务版本号
表示该快照有效,否则表示该快照已被删除。
1.3 Undo 日志
快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行的所有快照连接起来。
2 实现过程
- SELECT
- InnoDB 只查找版本早于当前事务版本的数据行
- 行的删除版本要么未定义,要么大于当前事务版本号。
- INSERT
- InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。
- 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃 copy(rollback)
- DELETE
- InnoDB 为删除的每一行保存当前系统版本号作为行删除标识。
- UPDATE
- 将当前系统版本号作为更新前的数据行快照的删除版本号,并将当前系统版本号作为更新后的数据行快照的创建版本号。可以理解为先执行 DELETE 后执行 INSERT。
- 修改时 Copy 出当前版本随意修改,各个事务之间无干扰。
3 快照读与当前读
3.1 快照读
使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。
select * from table ...;
3.2 当前读
读取的是最新的数据,需要加锁。以下第一个语句需要加 S 锁,其它都需要加 X 锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update;
delete;