走进迈莫

MVCC原理机制

2021-01-16  本文已影响0人  迈莫coding

目录

背景

写<<校招MySQL那些事>>系列文章,一方面帮助在校大学生可以提早知道大厂的面试过程,了解大厂究竟需要什么样人才,自己该如何进行准备,全力以赴;另一方面也是自我巩固知识,因为我也是从校招进入大厂的,自己也经历过痛并快乐的过程,也希望可以把我自己的经验,都吐露出来。

接下来我会写很多关于校招文章,比如<<校招Redis那些事>>,<<校招go那些事>>,<<校招kafka那些事>>等等,敬请期待。

前提回顾

什么是MVCC

多版本并发控制(MVCC),是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务戳关联,读操作只读事务开始前的数据库的快照。这样在读操作的时候不会阻塞写操作,写操作不会阻塞读操作的同时,也避免了脏读和不可重复读。

什么是当前读和快照读

在学MVCC多版本并发控制之前,先了解一下什么是mysql InnoDB下的当前读和快照读?

当前读,快照读和MVCC关系

MVCC,乐观锁,悲观锁关系

这样组合的方式最大程度的提高数据库并发性能,解决读写冲突以及写写冲突所导致的问题。

MVCC实现原理

MVCC就是多版本并发控制,用来解决读写冲突。它主要由隐藏字段,undolog日志,read-view来配合完成的。所以先看看这几个属性,理解其含义,便于理解MVCC实现原理。

隐藏字段

隐藏字段中除了咱们自定义的字段外,还隐含着其他属性字段,是系统默认给加上去的,比如roll_pointer,trx_id等字段。
roll_pointer
回滚指针,指向这条记录的上一个版本
trx_id
事务ID,记录创建/修改这条记录的事务ID,用于版本比较,从而找到快照

name age trx_id roll_pointer(回滚指针)
迈莫 22 1 0x1654u

如表中所示,name和age属性为用户自定义属性,而trx_id和roll_pointer就表示隐藏属性,数据库默认添加。在这表中,trx_id表示操作这条记录的事务ID,roll_pointer是回滚指针,表示指向上一个版本,一般配合undolog日志使用

undolog日志

undolog日志存储某条记录的所有操作,以链表方式将各个版本进行串联起来

举例

第一步: 比如有个事务1向person表中插入一条数据,记录如下,name为迈莫,age为22,事务ID为1,回滚指针假设为null,如下图所示
[图片上传失败...(image-52ed3b-1610512344090)]

第二步: 此时又来一个事务2,对该记录的age值进行修改,修改为23
首先将事务1的操作记录添加到undolog日志中
将事务2的操作记录作为数据的最新记录
将事务2中隐藏字段roll_pointer(回滚指针)指向事务1,进行串联

在这里插入图片描述

第三步: 又有一个事务3,对该记录的name值进行操作,修改为memolei
首先将事务2的操作记录迁移到undolog日志中
将事务3的操作记录作为数据的最新记录
将事务3中隐藏字段roll_pointer(回滚指针)指向事务2,进行串联

在这里插入图片描述

从上面图可以看到,每当有事务对该数据进行操作时,首先会将操作前最新数据迁移到undolog日志中,将当前事务操作的记录作为最新数据,并且将隐藏字段roll_pointer指向上一个版本

read-view(一致性视图)

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

版本链比对规则:

整体流程

前提条件

在这里插入图片描述

如图表所示,有四个事务,分别为translation2,translation3,translation4,translation5;translation2,translation3,translation4分别对数据库进行修改操作,translation5进行查询操作。

执行过程

  1. 当translation2对persion表中ID为1的数据进行修改时,首先会将该记录操作作为最新记录,并且roll_pointer指向上一个版本,但尤于translation2未commit,所以translation2仍然为未提交事务


    在这里插入图片描述
  2. 当translation3对persion表中ID为1的数据进行修改时,首先会将该记录操作作为最新记录,并且roll_pointer指向上一个版本,但尤于translation3未commit,所以translation3仍然为未提交事务


    在这里插入图片描述
  3. 当translation4对persion表中ID为1的数据进行修改时,首先会将该记录操作作为最新记录,并且roll_pointer指向上一个版本,但尤于translation4已commit,所以translation4为已提交事务


    在这里插入图片描述
  1. translation5进行select语句查询时,并且由于其是第一次创建查询语句,所以会创建read-view一致性视图。read-view一致性视图是由未提交事务ID数组和最大事务ID组成,由表中可知,当translation5进行查询时,translation2和translation3都为未提交事务,translation4为已提交事务,所以,read-view中未提交事务ID数组由translation2和translation3组成,最大事务ID为translation4,所以max_id为translation5,min_id为translation2
    [图片上传失败...(image-bf670e-1610512344090)]

  2. 接下来的话,就需要进行版本链比较(若不记得版本链比较规则回去温习一下),由于read-view由未提交事务数组[1,2]和最大事务ID3组成。由于当前最新记录事务ID为4,4大于最大事务ID3,所以无法查看;进行回溯,到undolog日志查询,事务3在未提交事务数组中,也就是中间这一段,由于事务三不在未提交事务数组中,说明事务3为已提交事务,因此是可见的,最终结果为name="memolei"


    在这里插入图片描述

到此,MVCC整个流程处理完了,一遍生,二遍熟,温故而知新。

彩蛋

RR和RC隔离级别下的InnoDB快照读有什么区别

总之在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。

闲聊

文章也会持续更新,可以微信搜索「 迈莫coding 」第一时间阅读。

上一篇 下一篇

猜你喜欢

热点阅读