阅读 239

InnoDB事务隔离性的实现——MVCC

概述

MySQL InnoDB基于ReadView(一致性快照)实现了MVCC,从而实现了非锁定读,极大地提高了数据库的并发性。同时InnoDB基于MVCC实现了READ COMMITTED 与REPEATABLE READ隔离级别。

分析

InnoDB MVCC实现原理

ReadView一致性视图

Innodb基于一致性视图consistent read view实现MVCC,我们通常称之为快照读(因为它是基于快照实现的),在InnoDB中默认的读取方式都是非锁定读,即不会占用和等待表或记录上的锁。在事务开始的时候,需要对MySQL整个库创建一份“快照”,但是这里的快照在实现上并不是对整个库拷贝一份数据,这样显然是不现实的。

其实InnoDB是基于ReadView这个数据结构来实现快照的,ReadView可用用来计算某一条记录对于当前事务是不是可见。

ReadView数据结构[1]^{[1]}中有下面几个核心属性:

         // 高水位,当前系统中的事务的最大id值加一,即下一个开启的事务的id,大于等于该id的事务都是在该快照创建之后开启的事务
	trx_id_t	m_low_limit_id;

	// 低水位,当前活跃事务的最小id,小于该id的事务都是已经提交的事务,所有data_trx_id < 该值 的记录对于当前事务是可见的
	trx_id_t	m_up_limit_id;

	// 创建该ReadView的事务ID
	trx_id_t	m_creator_trx_id;

	// 当前快照被创建时,活跃(已启动但未提交)的事务集合
	ids_t		m_ids;
复制代码

判断记录是否可见

InnoDB对于每一条记录都有维护下面几个隐藏列:

  1. data_trx_id,最新更改该行记录的事务id
  2. data_roll_ptr,指向该行的回滚段的指针,即undo中该行的历史记录链表
  3. data_row_id,仅当没有显式定义主键时,会默认生成一个data_row_id列存储自动生成的主键值

图片来源:极客时间《MySQL45讲》

InnoDB只需要取出该记录的data_trx_id,基于ReadView数据结构就可以计算出是否可见[2]^{[2]},判断可见的逻辑如下:

  1. data_trx_id ≥ 高水位,不可见,表示这个数据是由将来的事务更新的

  2. data_trx_id < 低水为,可见,表示这个数据是由已经提交的事务更新的

  3. 介于高水位与低水位之间

    1. 如果在活跃事务集合中,表示尚未提交的事务,不可见
    2. 如果不在,表示事务已经提交了,可见

如果记录不可见,需要通过记录的data_roll_ptr指针遍历undo log,计算出该行在当前事务中可见的历史版本。

事务隔离性的实现

InnoDB的隔离性其实也是基于MVCC实现,MVCC只在读已提交与可重复读的隔离级别下发挥作用,两者对于一致性快照定义是不一样的[4]^{[4]}

  1. 读已提交,对于事务中每一次快照读都会重新计算一下一致性事务;
  2. 可重复读,只会在事务开始的第一次快照读创建一致性事务,后续的读取都会使用这个一致性事务。

而对于另外两种:

  1. 读未提交,总是读取最新的版本,即使是未提交的版本记录
  2. 串行读,总是加读锁读取,不会存在多个版本

参考文档

  1. MySQL源码,ReadView,storage/innobase/include/read0types.h#ReadView
  2. MySQL源码,判断记录可见性,storage/innobase/include/read0types.h#changes_visible()
  3. 极客时间《MySQL45讲》,第八讲,事务到底是隔离的还是不隔离的?
  4. 《MySQL5.7官网文档》,14.7.2.3 Consistent Nonlocking Reads
文章分类
后端
文章标签