Mysql知识延展(七)MVCC多版本并发控制

MVCC简述

MVCC(Mutil-Version Concurrency Control),就是多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库读写的并发访问

在Mysql的InnoDB引擎中就是指在已提交读(READ COMMITTD)可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程。这就使得别的事务可以修改这条记录,反正每次修改都会在版本链中记录SELECT可以去版本链中读取记录,这就实现了读-写,写-读的并发执行,提升了系统的性能。

当前读和快照读

在MVCC并发控制中,读操作可以分成两类:

  • 快照读 (snapshot read)
    读取的是记录的可见版本 (有可能是历史版本),不用加锁。简单的select操作,属于快照读。
  • 当前读 (current read)
    读取的是记录的最新版本,并且当前读返回记录,都会加上锁,保证其他事务不会再并发修改这
    条记录。插入/更新/删除操作,属于当前读。
    Mysql知识延展(七)MVCC多版本并发控制

一致性非锁定读

一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(MVCC)读取当前数
据库中行数据的方式
如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行锁的释放。相反地,InnoDB会去读取行的一个最新可见快照
Mysql知识延展(七)MVCC多版本并发控制
若出现AB会话如下图所示:
Mysql知识延展(七)MVCC多版本并发控制
当会话B提交事务后,会话A再次运行 SELECT * FROM test WHERE id = 1 的SQL语句
时,两个不同的事务隔离级别下得到的结果可能就不一样了。

Mysql实现MVCC依赖undo log以及read view

版本链

InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列

  • trx_id
    这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id
  • roll_pointer
    每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)

事务链表

MySQL中的事务开始到提交这段过程中,都会被保存到一个叫trx_sys的事务链表中,这是一个基本
的链表结构:
ct-trx --> trx11 --> trx9 --> trx6 --> trx5 --> trx3;
事务链表中保存的都是还未提交的事务,事务一旦被提交,则会被从事务链表中摘除

  • RR隔离级别下,在每个事务开始的时候,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)
  • RC隔离级别下,在每个语句开始的时候,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)

show engine innodb status ,就能够看到事务列表。

ReadView

ReadView中主要就是有个列表存储我们系统中当前活跃着的读写事务,也就是begin了还未提交的事务。通过这个列表来判断记录的某个版本是否对当前事务可见。假设当前列表里的事务id为[80,100]。

  • 如果访问的记录版本的事务id为50比当前列表最小的id80小,那说明这个事务在之前就提交了,所以对当前活动的事务来说是可访问的
  • 如果访问的记录版本的事务id为90,发现此事务在列表id最大值和最小值之间,那就再判断一下是否在列表内,如果在那就说明此事务还未提交,所以版本不能被访问。如果不在说明事务已经提交,所以版本可以被访问
  • 如果访问的记录版本的事务id为110,那比事务列表最大id100都大,那说明这个版本在ReadView生成之后才发生的,所以不能被访问

这些记录都是去版本链里面找的先找最近记录,如果最近这一条记录事务id不符合条件,不可见的话再去找上一个版本再比较当前事务的id和这个版本事务id看能不能访问,以此类推直到返回可见的版本或者结束

参考:https://baijiahao.baidu.com/s?id=1629409989970483292&wfr=spider&for=pc