Mysql MVCC

数据库的两种读,每种读读的数据版本不一样(快照读和当前读),所以也称为MVCC,即多版本并发控制

版本链

又称事务链,每次修改数据的时候,都会记录一条undoLog日志,日志中记录数据每一次的变化,并且通过数据中的两个隐藏列

db_trx_id(事务id) 用标记每一条数据对应的事务id。
db_roll_ptr(指针) 用来串联版本链的指针。

db_row_id:隐藏主键,如果我们没有给这个表创建主键,那么会以这个字段来创建聚簇索引。

因为每一次记录变更之前都会先存储一份快照到undo log中,那么这几个隐式字段也会跟着记录一起保存在undo log中,就这样,每一个快照中都有有一个db_trx_id字段记录了本次变更的事务ID,以及一个db_roll_ptr字段指向了上一个快照的地址。(db_trx_id和db_roll_ptr是重点,后面还会用到)

这样,就形成了一个快照链表:

Mysql MVCC_第1张图片

有了undo log,又有了几个隐式字段,我们好像还是不知道具体应该读取哪个快照,那怎么办呢

这时候就需要Read View 登场了

ReadView

阅读视图,可以理解为某一次读取的时候,根据不同的事务生成的数据快照,并且除了表中的数据,还有一下这些固定字段。

  • trx_ids,系统当前未提交的事务 ID 的列表。

  • low_limit_id,应该分配给下一个事务的id 值。

  • up_limit_id,未提交的事务中最小的事务 ID。

  • creator_trx_id,创建这个 Read View 的事务 ID。

每开启一个事务,我们都会从数据库中获得一个事务 ID,这个事务 ID 是自增长的,通过 ID 大小,我们就可以判断事务的时间顺序。

那么,一个事务应该看到哪些快照,不应该看到哪些快照该如何判断呢? 关键点来了

其实原则比较简单,那就是事务ID大的事务应该能看到事务ID小的事务的变更结果,反之则不能!

假如当前有一个事务3想要对某条记录进行一次快照读的时候,他会先创建一个Read View,并且把当前所有还未提交的事务的信息记录下来。比如up_limit_id = 2,low_limit_id = 5,trx_ids= [2,4,5],creator_trx_id= 6(这里都是未提交的事务,牢记)
Mysql MVCC_第2张图片

ReadView生成规则

  • READ COMMITTED(读已提交): 在一个事务中,每次查询都会生成一个新的ReadView

  • REPEATABLE READ(可重复读) :在一个事务中只会在第一次执行查询语句时生成一个ReadView,之后的查询就不会重复生成了

ReadView校验规则

  • db_trx_id:对这条记录做了最新一次修改的事务的ID
  • low_limit_id:应该分配给下一个事务的id 值。
  • up_limit_id:未提交的事务中最小的事务 ID。
  1. 如果db_trx_id与ReadView中的creator_trx_id值相同,说明当前事务修改的记录就是在当前事务下操作的,那当然是对我们可见的了,因此可以修改这条记录

  2. 如果db_trx_id小于ReadView中up_limit_id值,说明(生成该版本的事务在该事务生成readView之间已经提交)即当前事务在开启的时候,这条记录最近一次被其他事务操作的事务已经提交了,所以对这条记录对我们来说也是可以见,可以修改

  3. 如果db_trx_id大于或者等于ReadView中low_limit_id值,说明(生成该版本的事务在当前事务生成readView之后才开启)即:我们开启事务未修改该记录之前,已经有另外一个事务开启,并且正在修改该事务了,因此,这条记录对我们来说依然是不可见的,我们不能修改

  4. 如果db_trx_id介于ReadView中 up_limit_id 和 low_limit_id 之间的话
    有两种情况,分析当db_trx_id是否在 trx_ids 中

    ① 如果在,说明(创建ReadView时,生成该版本的事务还处于活跃状态)即:当前已经有其它的事务正在修改该条记录,并且还未提交,此时这条记录对我们不可见

    ② 如果不在,说明(创建ReadView时,生成该版本的事务已经提交)即:此时没有事务操作该条记录,我们可以修改该条记录

当数据的事务ID不符合Read View规则时候,也就是不可见的时候,那就需要从undo log里面通过版本链的回滚指针找到这条记录的上一个版本在进行判断,直到遍历版本链找到对当前查询可见的版本返回,找不到则返回空。

你可能感兴趣的:(mysql,mysql,数据库)