MVCC简述
MVCC(Mutil-Version Concurrency Control)
,就是多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库读写的并发访问
。
在Mysql的InnoDB
引擎中就是指在已提交读(READ COMMITTD)
和可重复读(REPEATABLE READ)
这两种隔离级别下的事务对于SELECT
操作会访问版本链中的记录
的过程。这就使得别的事务可以修改这条记录
,反正每次修改都会在版本链中记录
。SELECT
可以去版本链中读取记录
,这就实现了读-写,写-读的并发执行
,提升了系统的性能。
当前读和快照读
在MVCC并发控制中,读操作可以分成两类:
快照读
(snapshot read)
读取的是记录的可见版本
(有可能是历史版本),不用加锁
。简单的select操作,属于快照读。当前读
(current read)
读取的是记录的最新版本
,并且当前读返回
的记录
,都会加上锁
,保证其他事务不会再并发修改这
条记录。插入/更新/删除
操作,属于当前读。
一致性非锁定读
一致性非锁定读
(consistent nonlocking read)是指InnoDB
存储引擎通过多版本控制
(MVCC)读取
当前数
据库中行数据的方式
。
如果读取的行正在执行DELETE或UPDATE
操作,这时读取操作不会
因此去等待行锁的释放
。相反地,InnoDB
会去读取行的一个最新可见快照
。
若出现AB会话如下图所示:
当会话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