暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL MVCC(多版本控制)

248



Hi~朋友,关注置顶防止错过消息


事务的启动时间是啥时候?

  • begin/start transaction命令不是一个事务的起点,而是在执行到第一个操作InnoDB表的语句时,事务才真正启动。
  • 如果想马上启动一个事务,可以执行start transaction with consistent snapshot命令

什么是一致性读视图?

一致性读视图是InnoDB在实现MVCC用到的视图,用于读提交(RC)和可重复度(RR)隔离级别的实现。

一致性视图没有物理结构,主要是在事务执行期间用来定义该事物可以看到什么数据。

什么是row trx_id?

事务在正式启动的时候我们会创建一致性视图,该一致性视图是基于整个库的。该一致性视图不会拷贝整个数据库的数据(因为拷贝数据是不现实的)。

InnodDB的每个事务都有一个唯一的事务ID,叫做transaction id,该ID在事务开始的时候向InnoDB申请,并且按照申请顺序严格递增。

每行数据都会有多个版本,每次事务更新数据的时候都会生成一个新的数据版本,并且把transaction id赋值给这个数据版本的事务id,称为row trx_id。


如上图所示,是一行记录被多个事务更新后的状态,该行记录的最新版本是有由transaction id为25的事务更新的,因此row trx_id也为25。

U3、U2、U1代表的是undo log,V1、V2、V3在物理上并不真实存在,而是在需要的时候通过V4配合undo log计算获得。

如何构建一致性读视图

InnoDB会为每个事务构造一个数组,该数组用来保存事务启动瞬间当前正在活跃(启动还未提交)的所有事务ID。

数组里面事务ID的最小值为低水位,当前系统里面已经创建过的事务ID的最大值加1位高水位。该视图数组和高水位就组成了当前事务的一致性视图。


对于当前事务的启动瞬间,一个数据版本的row trx_id会有以下几种可能:

  • 如果在绿色部分,表示该版本是已提交的事务或者是自己生成的,数据可见
  • 如果落在红色部分,表示该版本是由未来的事务生成的,数据不可见
  • 如果落在黄色部分,如果row trx_id在数组中,表示该版本是由还没提交的事务生成,数据不可见;如果row trx_id不在数组中,表示该版本是已经提交了的事务生成的,可见。

上述是代码逻辑的,我们可以简化一下,一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以为,有以下几种情况:

  1. 版本未提交,不可见
  2. 版本已提交,但是是在视图创建以后提交的,不可见
  3. 版本已提交,而且是在视图创建以前提交的,可见

update逻辑和select逻辑的不同


假设id=1的k初始值为1,隔离级别为可重复读,大家可以试着分析以上三个事务的结果:

  • 事务A:k为1
  • 事务B:k为3
  • 事务C:k为2

根据我们上面将的多版本控制和一致性视图,事务A很容易得出1,事务C很容易得出2,但是如果按照上述分析,事务B的结果会和预想的不太一致,这是因为事务B中出现了update这行记录,update和单纯的select将会有些许不同。

update在更新数据时候不能在历史版本上进行更新的,也就是说事务B是在k=2的基础上进行更新,此时就需要用到一条规则:更新数据都是先读后写,并且这个读为当前读。

除了update语句以为,select如果加锁也是当前读,如下:

-- 加读锁
select k from t where id = 1 lock in share mode;
-- 加写锁
select k from t where id = 1 for update;


我们对之前的事务C做些改造,如上图,改造完成以后会发生如下情况:

  • 事务C还没提交,因此在(1,2)这个版本上的写锁还没有释放
  • 事务B又因为是当前读,必须读最新版本,而且必须加锁,因此就阻塞等待事务C释放该锁,才能继续它的当前读

事务如何实现MVCC?

  1. 每一个事务都有一个事务ID(transaction id),该ID严格递增
  2. 事务在启动时,会找到已提交的最大事务ID即为up_limit_id
  3. 事务在更新一条语句时,比如id=1改为了id=2,会把id=1和该行之前的row trx_id写到undo log中,并且在数据页将id的值修改为2,并且把修改这条语句的transaction id记在该行行头
  4. 一个事务要查看一条数据时,必须用该事物的up_limit_id与该行的transaction id做对比,如果up_limit_id>=transaction id,该数据可见,如果up_limit_id<transaction id则需要去undo log里面去取,去undo_log查找数据的时候,也需要做对比,只有up_limit_id>transaction id才可以返回数据
  5. 当前读是先读后写,会更新事务内的up_limit_id为该事务的transaction id


本期MySQL MVCC就到这,扫码关注,更多内容我们下期再见!



往期推荐

MySQL锁

MySQL事务

update执行流程(MySQL redo log和binlog详解)

select语句执行流程

输入网址到回显页面,经历了哪些过程

一文带你搞定IP相关协议


文章转载自程序员修炼笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论