MySQL是怎样运行的(八)

版本链

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

  1. DB_TRX_ID:一个事务每次对某条聚簇索引记录进行改动时,都会把该事务的事务ID赋值给DB_TRX_ID隐藏列
  2. DB_ROLL_PTR:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到Undo Log中。这个隐藏列就相当于一个指针,可以通过它找到该记录修改前的信息

每对记录进行一次改动,都会记录一条Undo Log。每条Undo Log也都有一个roll_pointer属性(INSERT除外,因为新插入的纪录没有更早的版本),通过这个属性可以将这些Undo Log串成一个链表,这个链表称为版本链。版本链的头节点是当前记录的最新值,另外,每个版本中还包含生成该版本时对应的事务ID。

ReadView

  对于使用READ UNCOMMITTED隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了;对于使用SERIALIZABLE隔离级别的事务来说,InnoDB使用加锁的方式来保证对数据的串行访问;对于使用READ COMMITTEDREAPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交的事务修改过的记录。也就是说,假如另一个事务已经修改了记录但尚未提交,则不能直接读取最新版本的记录。核心的问题是:需要判断版本链中的哪个版本是当前事务可见的。为此InnoDB提出了ReadView的概念,ReadView中主要包含4个比较重要的内容:

  1. m_ids:在生成ReadView时,当前系统中活跃的读写事务的事务ID列表,所谓活跃就是还未提交或回滚
  2. min_trx_id:在生成ReadView时,当前系统中活跃的读写事务中最小的事务ID,也就是m_ids中的最小值
  3. max_trx_id:在生成ReadView时,系统应该分配给下一个事务的事务ID值。注意max_trx_id并不是m_ids中的最大值,比如现在有事务ID为1、2、3的三个事务,之后事务ID为3的事务提交了,那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值是1,而max_trx_id的值是4
  4. creator_trx_id:生成该ReadView的事务的事务ID。注意只有对表中的记录进行改动时才会为事务分配一个唯一的事务ID,否则默认为0

有了ReadView后,在访问某条记录时,只需要按照下面的步骤来判断记录的某个版本是否可见:

  1. 如果被访问版本的DB_TRX_ID属性值与ReadViewcreator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问
  2. 如果被访问版本的DB_TRX_ID属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问
  3. 如果被访问版本的DB_TRX_ID属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView之后才开启,所以该版本不可以被当前事务访问
  4. 如果被访问版本的DB_TRX_ID属性值在ReadViewmin_trx_id值和max_trx_id值之间,则需要判断DB_TRX_ID属性值是否在m_ids列表中。如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经提交,该版本可以被访问
  5. 如果某个版本的数据对当前事务不可见,那就顺着版本链找到下一个版本的数据,继续执行上述步骤来判断记录的可见性;依此类推,直到版本链中的最后一个版本。如果记录的最后一个版本也不可见,就意味着该条记录对当前事务完全不可见,查询结果就不该包含该记录

MySQL中,READ COMMITTEDREAPEATABLE READ隔离级别之间一个非常大的区别就是它们生成ReadView的时机不同:

  1. READ COMMITTED:每一次进行普通SELECT操作前都会生成一个ReadView
  2. REAPEATABLE READ:只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView

只有进行普通的SELECT查询时,MVCC才会生效;而不普通的SELECT,比如SELECT ... FOR UPDATE是加锁查询的,而MVCC是对读操作的无锁优化,自然不会放到一起用。

Reference

  摘抄自《MySQL是怎样运行的——从根儿上理解MySQL》,小孩子4919 著。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!