14、多版本并发控制


多版本并发控制

什么是MVCC

1、多版本并发控制(MVCC)解决了脏读、不可重复读、幻读的问题,保证了事务的隔离性,实现了数据的一致性读,。
2、MVCC实现依赖于

  • 隐藏字段(trx_id、roll_pointer)、undo log:实现了多版本
  • readview:实现了并发控制

快照读和当前读

2.1快照读

  • 快照读又叫一致性读,读取的是历史版本的数据。不加锁的简单的SELECT都属于快照读,即不加锁的非阻塞读。
  • 快照读的实现是基于MVCC,它在很多情况下,避免了加锁操作,降低了开销,提高了数据库的并发性能。

2.2当前读

  • 当前读读取的是记录的最新数据。加锁的SELECT、对数据进行增删改都会进行当前读。
  • 当前读的实现是基于锁,读取时其他并发事务不能修改和读取当前记录。

隐藏字段和undo log版本链

3.1隐藏字段

对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列。

  • trx_id :每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的 事务id赋值给trx_id 隐藏列。
  • roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

insert undo log只在事务回滚时起作用;当事务提交后,该类型的undo日志就会被删除,它占用的UndoLog Segment也会被系统回收。
update undo log用于进行事务的回滚;当事务提交后该日志就会放入undo log版本链中用于实现MVCC。

3.2undo log版本链的生成

  • 两个事务id分别为10、20的事务对id为1的记录进行UPDATE操作。
  • 对该记录每次更新后,都会将旧值放到一条undo日志中,每条undo日志也都有一个roll_pointer属性,roll_pointer属性连接成一个undo log版本链。

readview

4.1什么是readview

readview就是事务在使用MVCC机制进行快照读操作时产生的读视图。使用readview可以实现对undo log版本链的控制。

4.2readview的组成

MVCC只能在READ COMMITTED和REPEATABLE READ隔离级别下使用,可以读取到已经提交的数据,对未提交的数据是不可读的。
readview中包含的内容:

  • creator_trx_id:创建这个Read View的事务ID。

只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。

  • trx_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表 。
  • up_limit_id:活跃的事务中最小的事务ID。
  • low_limit_id:表示生成ReadView时系统中应该分配给下一个事务的id值。(系统中最大的事务id加上1就是low_limit_id)

low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是4。

4.3readview的规则

这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • 如果被访问版本的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时生成该版本的事务已经被提交,该版本可以被访问。

4.4readview的生成时机

READ COMMITTD和REPEATABLE READ这两个隔离级别的一个很大不同就是生成ReadView的时机不同:

  • READ COMMITTD:在每一次进行普通SELECT操作前都会生成一个ReadView

  • REPEATABLE READ:只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用第一次的ReadView就好了。

4.5MVCC操作的整体流程

查询一条记录:

  1. 首先获取事务自己的版本号,也就是事务 ID;
  2. 获取该事务的ReadView;
  3. 查询得到的数据的事务id,然后与ReadView的规则进行比较;
  4. 如果不符合ReadView规则,就需要从Undo Log版本链中获取下一个版本的数据;
  5. 最后返回符合规则的数据。

相关