1.What & Why
- MVCC 多版本并发控制,多个事务并发进行时,对于每行每个事务有自己的Read View版本,这样就无需给行加读锁,就不会阻塞写操作,以获得更大的并发度。
- 多版本,一条记录存在这多个版本,某时刻最新的版本存储在数据页上,历史版本存储在undo log回滚段中,每行数据和历史版本有隐藏列DATA_TRX_ID和DATA_ROLL_PTR和旧数据。
DATA_TRX_ID表示更新这个版本的事务id(版本号);
DATA_ROLL_PTR指向上个版本,构成版本链;。
- trx_id事务id,每个事务begin开始时获取的全局唯一的连续递增的序列号(也称为版本号)
- Read View 视图,每个事务(RR,可重复读)或每条语句(RC,读已提交)会在开始时创建的结构体,用于版本可见性控制。包含
creator_trx_id | 当前事务id |
up_limit_id | 活跃的最小事务id |
low_limit_id | 活跃的最大事务id |
trx_ids | 活跃事务列表 |
2.版本控制算法
- time1:开启事务,扫描事务链表trx_sys(当前活跃的事务),选取事务id构建Read View
creator_trx_id | 当前事务id |
up_limit_id | 活跃的最小事务id |
low_limit_id | 活跃的最大事务id |
trx_ids | 活跃事务列表 |
- time 2: select 查询数据 ,从数据页的数据行(版本链的head版本)开始遍历,如果该版本可见(比较版本号和ReadView)返回,如果不可见走到下一跳(上个版本)。
- times:可见? 比较版本的DATA_TRX_ID和up_limit_id、low_limit_id,判断DATA_TRX_ID在不在trx_ids列表中,得出结论当前事务能否看见这个版本。
2.1 select 查询数据伪代码
select(row, readView){
node = row //数据页行记录
while(node.roll_ptr!=null
&& canSee(node.trx_id, readView)) { // 顺着版本链找到最新的可见的版本
node = *roll_ptr
}
return node.oldVal // 版本值
}
2.2 可见性算法伪代码
canSee(trx_id, readView){
if(trx_id < readView.up_limit_id) //这些数据在事务创建id的时都已经提交
return true;
if(trx_id >= readView.low_limit_id) //该事务在当前事务开始后开始
return false
if(up_limit_id<trx_id<low_limit_id){
if(隔离级别==可重复读RR)
return false
if(隔离级别==读已提交RC) {
if(read_view->trx_ids.contains(trx_id))
return false //修改当前版本的事务还活跃,未提交,不可见
else{
return true // 该事务已提交,RC可读
}
}
}
}