MVCC篇

MVCC(多版本并发控制)是一种乐观锁机制,用于实现RR和RC隔离级别。在InnoDB引擎中,每行数据包含版本号和回滚指针,通过快照读和版本链比对规则实现并发控制。在RR隔离级别下,事务内多次查询结果相同,而在RD隔离级别下每次查询可能看到不同数据。删除操作实际上是更新版本并标记删除。MVCC确保了高并发场景下的数据一致性。
摘要由CSDN通过智能技术生成

MVCC简介

​ MVCC:Multi-Version Concurrency Control多版本并发控制,不仅用于MySQL,分布式事务也可以使用;是一种乐观锁,用于RR(可重复读)、RC(读已提交)隔离级别。使用了行级锁。

​ 当执行查询sql时会生成一致性视图read-view,它由执行查询时所有未提交事务id数组(数组里最小id为min_id)和已创建的最大事务id(max_id)组成,查询的数据结果需要跟read-view做比对从而得到快照结果。

​ MVCC通过保存数据在某个时间点的快照来实现的,基本特征如下:

  • 每行数据都存在一个版本,每次数据更新时都更新该版本。
  • 修改时Copy出当前版本随意修改,各个事务之间互不干扰。
  • 保存时比较版本号,如果成功commit则覆盖原记录;失败则放弃copy。

2、InnoDB引擎的MVCC策略

​ 每行数据额外保存两个隐藏列(当前行创建时的版本号和删除时的版本号,另外还有一列称为回滚指针,用于事务回滚);

​ InnoDB内部为每一行添加了两个隐藏列:DB_TRX_ID版本号DB_ROLL_PTR回滚指针(MySQL另外还有一个隐藏列DB_ROW_ID,这是在InnoDB表没有主键的时候会用来作为主键)。

  • DB_TRX_ID:长度为6字节,存储了插入或更新语句的最后一个事务的事务ID。
  • DB_ROLL_PTR:长度为7字节,称之为:回滚指针。回滚指针指向写入回滚段的undo log记录,读取记录的时候会根据指针去读取undo log中的记录。
  • DB_ROW_ID: 行标识(隐藏单调自增 ID ),大小为 6 字节,如果表没有主键, InnoDB 会自动生成一个隐藏主键,因此会出现这个列。另外,每条记录的头信息( record header )里都有一个专门的 bit ( deleted_flag )来表示当前记录是否已经被删除。

​ 快照读:在RR隔离级别下,在不加锁的情况下MySQL会根据回滚指针选择从undo log记录中获取快照数据,而不总是获取最新的数据,这也就是为什么另一个事务提交了数据,在当前事务中看到的依然是另一个事务提交之前的数据。RR隔离级别快照并不是在BEGIN就开始产生了,而是要等到事务当中的第一次查询之后才会产生快照,之后的查询就只读取这个快照数据。

3、版本链比对规则

规则描述1

事务规则:

从最新记录开始查找:

  • 如果,当前记录的事务id<未提交事务的最小id;说明事务都是已提交的,可读
  • 如果,未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id;事务id是否在未提交事务id数组中,若在则不可读(但可以读自己本事务的)。
  • 如果,当前记录的事务id>事务的最大id;事务还未开始,不可读。

RR(可重复读):返回的readview是第一条记录的,在事务中不会重复生成。

RD(读已提交):每次查询都生成最新的readview。

规则描述2

1、如果落在绿色部分(trx_id<min_id),表示这个版本是已经提交的事务生成的,这个数据是可见的;

2、如果落在红色部分(trx_id>max_id),表示这个版本是由将来启动的事务生成的,是肯定不可见的;

3、如果落在黄色部分(min_id<=trx_id<=max_id),那就包括两种情况:

  • 若row的trx_id在数组中,表示这个版本是由还没有提交的事务生成的,不可见,当前自己的事务是可见的。
  • 若row的trx_id不再数组中,表示这个版本是已经提交了的事务生成的,可见。

 

删除的实现

​ 对于删除的情况可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(record header)里的(delete_flag)标记位写上true,来表示当前记录已经被删除,在查询时按照上面的规则查找到对应的记录,如果delete_flag标记位为true,意味着记录已被删除,则不返回数据。

​ 版本链生成是全局的不是单一表的,这些版本链记录在undo日志中。rc隔离级别下是多个select是中途更新read-view快照的。而RR隔离级别是不更新read-view的,因此可重复读。

4、案例分析

事务过程:

 

 

事务4的分析过程:

1、select name from table where id=1; readview:[1,3] 3

从undo日志的首行开始:

​ trx_id=1,属于未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id,1在其中,所以不可读。继续向下。

​ trx_id=3,属于未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id,3在其中,所以不可读。继续向下。

​ trx_id=2,属于未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id,2不在其中,所以可读。完成,返回name=B这条记录。

2、select name from table where id=1; readview:[1] 3

RR:沿用上个readview:[1,3] 3;查询结果仍旧不变(这就是为什么叫做可重复读)。

RD

trx_id=1,属于未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id,1在其中,所以不可读。继续向下。

trx_id=3,属于未提交事务的最小id<=当前记录的事务id<=未提交事务的最大id,3不在其中,所以可读。返回结果name=C这条记录。(这就是为什么叫做读已提交)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值