Mysql——MVCC的理解

MVCC

Mutil-Version Concurrency Control,就是多版本并发控制。

MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。

在Mysql的InnoDB引擎(仅InnoDB引擎支持事务)中就是指在已提交读(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程。

Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。

版本链

我们先来理解一下版本链的概念。在InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列:

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

在undo日志中就存在版本链

Mysql——MVCC的理解_第1张图片

ReadView

已提交读和可重复读的区别就在于它们生成ReadView的策略不同

ReadView中主要包含4个比较重要的内容:
1. m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
2. min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小
值。
3. max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
4. creator_trx_id:表示生成该ReadView的事务的事务id。
 


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

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

  1. 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  2. 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  3. 如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  4. 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问

READ COMMITED实现原理

每次读取数据前都生成一个ReadView。此时m_ids(活动事务id列表就会发生变化)。

REPEATABLE READ

在第一次读取数据时生成一个ReadView。

举例:

假设当前列表里的事务id为[80,100]。

如果你要访问的记录版本的事务id为50,比当前列表最小的事务id为80的小,那说明这个事务在之前就提交了,所以对当前活动的事务来说是可访问的。

如果你要访问的记录版本的事务id为90,发现此事务在列表id最大值和最小值之间,那就再判断一下是否在列表内,如果在那就说明此事务还未提交,所以版本不能被访问。如果不在那说明事务已经提交,所以版本可以被访问。

如果你要访问的记录版本的事务id为110,那比事务列表最大事务id100都大,那说明这个版本是在ReadView生成之后才发生的,所以不能被访问。这些记录都是去版本链里面找的,先找最近记录,如果最近这一条记录事务id不符合条件,不可见的话,再去找上一个版本再比较当前事务的id和这个版本事务id看能不能访问,以此类推直到返回可见的版本或者结束。

举个例子 ,在已提交读隔离级别下:

比如此时有一个事务id为100的事务,修改了name,使得的name等于小明2,但是事务还没提交。则此时的版本链是

Mysql——MVCC的理解_第2张图片

那此时另一个事务发起了select 语句要查询id为1的记录,那此时生成的ReadView 列表只有[100]。那就去版本链去找了,首先肯定找最近的一条,发现trx_id是100,也就是name为小明2的那条记录,发现在列表内,所以不能访问。

这时候就通过指针继续找下一条,name为小明1的记录,发现trx_id是60,小于列表中的最小id,所以可以访问,直接访问结果为小明1。

那这时候我们把事务id为100的事务提交了,并且新建了一个事务id为110也修改id为1的记录,并且不提交事务

Mysql——MVCC的理解_第3张图片

这时候版本链就是

Mysql——MVCC的理解_第4张图片

这时候之前那个select事务又执行了一次查询,要查询id为1的记录。

这个时候关键的地方来了

如果你是已提交读隔离级别,这时候你会重新一个ReadView,那你的活动事务列表中的值就变了,变成了[110]。

按照上的说法,你去版本链通过trx_id对比查找到合适的结果就是小明2。

如果你是可重复读隔离级别,这时候你的ReadView还是第一次select时候生成的ReadView,也就是列表的值还是[100]。所以select的结果是小明1。所以第二次select结果和第一次一样,所以叫可重复读!

总结最重要结论:

  • 也就是说已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

  • 这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。
     

 

你可能感兴趣的:(MySQL)