MVCC实现原理主要是依赖记录中的 3个隐式字段
,undo日志
,Read View
来实现的。
某条记录
name | grade | DB_ROW_ID | DB_TRX_ID | DB_ROLL_PTR |
zhangsan | A | 1 | 5 | null |
三个隐式字段:
DB_ROW_ID:隐含的自增ID(隐藏主键)
DB_TRX_ID:记录最近修改这条记录的事务ID
DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本
undo log:
将之前的操作都记录下来,适当时候进行回滚。(通过回滚指针进行回滚)
比如事务6将grade更改为A+:
name | grade | DB_ROW_ID | DB_TRX_ID | DB_ROLL_PTR |
zhangsan | A+ | 1 | 6 | 0x123456 |
name | grade | DB_ROW_ID | DB_TRX_ID | DB_ROLL_PTR |
zhangsan | A | 1 | 5 | null |
ReadView:
读视图,即事务进行快照读时刻,生成的数据库记录的快照。
可以将ReadView简化出三个属性:
list:记录生成ReadView时刻,活跃的事务ID
up_limit_id:list中最小的ID
low_limit_id:生成ReadView时刻,下一个未被分配的事务ID
基本流程:
- 当某事务查看记录时候,先拿该记录的DB_TRX_ID与up_limit_id(list中最小的ID)比较,如果DB_TRX_ID<up_limit_id,满足可见性,说明当前事务能看到DB_TRX_ID的记录,如果不小于进入下一轮;
- 拿该记录的DB_TRX_ID与low_limit_id(生成ReadView时刻,下一个未被分配的事务ID)比较,如果大于等于,则说明不可见,回滚指针将回滚上一个版本,重新进行判断。如果小于进入下一轮判断;
- 如果DB_TRX_ID在list(活跃事务中),则不可见,回滚指针回滚,不在的话,说明可见。
为了进一步让大家了解MVCC原理,在下面我举了个例子:
启动事务A,事务id为20,ReadView如下:
当事务A查看下面这条记录:
由于19>18,进入下一轮判断;
19 !>= 23,进入下一轮判断;
19 in List,回滚指针,选择该记录上一个版本再进行判断;
17<18,可见,说明事务A可以看到DB_TRX_ID=17的版本。
在这里,我利用一个代表DB_TRX_ID的数轴,绿色为可见,红色为不可见,可以利用这个数轴作为万能公式。