多版本并发控制:ReadView
-
MVCC 的实现依赖于:隐藏字段、Undo Log、Read View
-
简介
在MVCC机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在Undo Log里。如果一个事务想要查询这个行记录,
需要读取指定版本的行记录,这时就需要用到ReadView了,它帮我们解决了行的可见性问题。
ReadView就是事务在使用MVcc机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务构造了一个数组,
用来记录并维护系统当前活跃事务的ID(“活跃”指的就是,启动了但还没提交)。
- 设计思路
使用READ UNCOMMITTED隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。
使用SERIALIZABLE隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。
使用READ COMMITTED和REPEATABLE READ隔离级别的事务,都必须保证读到已经提交了的事务修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,
核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。
creator_trx_id,创建这个Read View的事务ID。
trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
up_limit_id,活跃的事务中最小的事务ID。
low_limit_id,表示生成ReadView时系统中应该分配给下一个事务的id值。low_limit_id是系统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。
- ReadView的规则
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。
如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值小于ReadView中的up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值大于或等于ReadView中的low_limit_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
如果被访问版本的trx_id属性值在ReadView的up_limit_id和low_limit_id之间,那就需要判断一下trx_id属性值是不是在trx_ids列表中。
如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。
如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问
- MVCC整体操作流程
了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:
首先获取事务自己的版本号,也就是事务ID;
获取ReadView;
查询得到的数据,然后与ReadView中的事务版本号进行比较;
如果不符合ReadView规则,就需要从Undo Log中获取历史快照;
最后返回符合规则的数据。
如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。
如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。