MVCC即多版本并发控制,是InnoDB等存储引擎实现高并发读写的重要机制。它通过保存数据的历史版本,使不同事务可以在不加锁的情况下读到一致的数据快照。整个过程主要依赖Undo Log版本链与Read View两个核心组件协同工作。
Undo Log版本链是什么
每当一行数据被修改时,数据库并不会直接覆盖旧值,而是将修改前的记录写入Undo Log。同时,行记录中有一个隐藏的回滚指针,指向该行的上一个历史版本。这样多次修改后,所有旧版本通过指针串成一条链,就是Undo Log版本链。
假设有一行数据初始值为A,事务一将其改为B,事务二又改为C,此时版本链从新到旧依次为C指向B,B指向A。每个版本还带有生成它的事务ID。
Read View的作用
Read View是事务在执行快照读时生成的一个视图。它记录了当前活跃的事务ID列表、当前最大事务ID等信息。通过Read View,事务可以判断版本链中的某个版本是否对自己可见。
- 如果版本的事务ID小于Read View中的最小活跃事务ID,说明该版本已提交,可见。
- 如果大于最大事务ID,说明是未来事务修改的,不可见。
- 如果在活跃列表中,说明尚未提交,不可见,需顺着版本链找更早的版本。
二者如何配合实现MVCC
当事务发起查询时,先拿到Read View,然后沿着Undo Log版本链从最新版本开始逐个判断。找到第一个对当前Read View可见的版本,作为查询结果返回。这样读不阻塞写,写也不阻塞读。
简单代码示例说明判断逻辑
// 伪代码:判断版本是否可见
boolean isVisible(long trxId, ReadView view) {
if (trxId < view.minTrxId) {
return true; // 已提交,可见
}
if (trxId > view.maxTrxId) {
return false; // 未来事务,不可见
}
if (view.activeSet.contains(trxId)) {
return false; // 活跃未提交,不可见
}
return true;
}
不同隔离级别下的差异
在可重复读隔离级别中,事务只在第一次快照读时生成Read View,后续复用,因此能看到稳定快照。而在读已提交级别中,每次快照读都会生成新的Read View,所以能读到其他事务最新提交的数据。
| 隔离级别 | Read View生成时机 | 可见性特点 |
|---|---|---|
| 读已提交 | 每次查询 | 看到最新已提交 |
| 可重复读 | 首次查询 | 整个事务一致 |
理解Undo Log版本链与Read View的配合,就能明白MVCC如何在无锁情况下保证事务隔离与并发性能。