MVCC中的READ VIEW

首先我们要先清楚READ VIEW是啥

READ VIEW 是快照,就是像拍照一样 把这一瞬间的景象(数据)保存下来

快照一旦形成就不会改变 只能当前快照声明周期了 再生成新的快照

我们的事务隔离等级中的 可重复读和读提交都是通过READ VIEW实现的 

那 Read View 到底是个什么东西?

m_ids:指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务

min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。

max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;

creator_trx_id :指的是创建该 Read View 的事务的事务 id

这是InnoDB储存的行结构

其中有三个隐藏列

row_id 如果我们建表的时候指定了主键或者唯一约束列,那么就没有 row_id 隐藏字段了。如果既没有指定主键,又没有唯一约束,那么 InnoDB 就会为记录添加 row_id 隐藏字段。

trx_id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务的事务 id 记录在 trx_id 隐藏列里

roll_ptr,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录。

在创建 Read View 后,我们可以将记录中的 trx_id 划分这三种情况:

一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:

  • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 已经提交的事务生成的,所以该版本的记录对当前事务可见
  • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 才启动的事务生成的,所以该版本的记录对当前事务不可见
  • 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
    • 如果记录的 trx_id  m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见
    • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见

哦哦哦 我终于转过弯来了 是执行一个事务的时候创建一个READ VIEW(「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View,而「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View。)

这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)。

细说读提交和可重复读

读提交

读提交隔离级别是在每次读取数据时,都会生成一个新的 Read View

假设事务 A (事务 id 为51)启动后,紧接着事务 B (事务 id 为52)也启动了,接着按顺序执行了以下操作:

  • 事务 B 读取数据(创建 Read View),小林的账户余额为 100 万;
  • 事务 A 修改数据(还没提交事务),将小林的账户余额从 100 万修改成了 200 万;
  • 事务 B 读取数据(创建 Read View),小林的账户余额为 100 万;
  • 事务 A 提交事务;
  • 事务 B 读取数据(创建 Read View),小林的账户余额为 200万

我们来思考一下 为什么会这样

首先 当读取数据时 创建一个READ VIEW 此时READ VIEW:(事务A已经启动)

creator_trx_id: 52

m_ids:51,52

min_trx_id:51

max_trx_id:53

此时命中

 但是此时还没发生改动 所以无所谓

第二次读取事务 READ VIEW与上次相同 但是这次发生了数据修改 此时trx_id在m_ids列表中 也就表示不可见(和上次命中一样等待逻辑空间) 沿着 undo log 链条往下找旧版本的记录,直到找到 trx_id 「小于」事务 B 的 Read View 中的 min_trx_id 值的第一条记录,所以事务 B 能读取到的是 trx_id 为 50 的记录,也就是小林余额是 100 万的这条记录。

 第三次读取事务的时候 A已经提交了事务 所以READ VIEW参数为

creator_trx_id: 52

m_ids:52

min_trx_id:52

max_trx_id:53

之前我没想明白啥意思 为啥会有

这种情况 我现在明白了 当当前READ VIEW存在期间 某个事务结束了 但是最早的事务还没结束

所以min_trx_id 没变 结束的id还是>最小的id 但是已经结束了(不在m_dis里面了)

此时51满足分支

 可见 也就查到了 200万的记录

一旦READVIEW 确定下来就不会更改了 这里的只不过是重复创建而已

可重复读

 可重复读隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View

还是先创建个A事务(id:51) B事务(id:52)

A事务的READ VIEW

creator_trx_id: 51

m_ids:51

min_trx_id:51

max_trx_id:52

B事务的READ VIEW

creator_trx_id: 52

m_ids:51 52

min_trx_id:51

max_trx_id:53

接着,在可重复读隔离级别下,事务 A 和事务 B 按顺序执行了以下操作:

  • 事务 B 读取小林的账户余额记录,读到余额是 100 万;
  • 事务 A 将小林的账户余额记录修改成 200 万,并没有提交事务;
  • 事务 B 读取小林的账户余额记录,读到余额还是 100 万;
  • 事务 A 提交事务;
  • 事务 B 读取小林的账户余额记录,读到余额依然还是 100 万;

 我们来看B的第一次读取 没区别 还没更改

第二次读取的收 用的还是最开始的READ VIEW 51在列表中 不可见 所以还是通过rool_ptr找上一个版本

第三次用的 还是这个快照 没区别

猜你喜欢

转载自blog.csdn.net/chara9885/article/details/131647638
今日推荐