【数据库】—— Mysql实现事务隔离级别的方式 — MVCC

前言

  结合前面提到的LBCC,和今天的MVCC,是实现事务隔离级别的两种方式

  但,怕是有些兄弟误会,他们两个是共同来维护mysql的一个隔离级别的,可以说是使用与不同的情况,并不是彼此替代的请情况

  涉及到的知识点

  undo log、redo log;

  当前读、快照读

  关于事务这块的我自己的思维导图 和大家分享一下

【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第1张图片

正文

   什么是MVCC

  Multiversion concurency control(多版本并发控制)

  并发访问(读或写)数据库时,对正在事务内处理的数据做多版本的管理。以达到用来避免写操作的堵塞,从而引发读操作的并发问题。

 

  我想大多数的可能就直接抛结论了,直接undo,redo就开始抛了,但我这需要先和大家聊些别的。

  MVCC的逻辑流程

  其实在每个表中都会加有隐藏字段,其中这里用到的有两个:

  DB_TRX_ID(数据行的版本号)、DB_ROLL_PT(删除版本号)

  还有一点需要提前说的是,系统有一个全局的事务id,每开启一个事务,都有他自己对应的事务id,自增。

  然后下面会以一个流程的的形式对表进行几条数据的操作,想了解的,最好不要跳过

  插入

  【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第2张图片

  首先我们有一个空表teacher,表结构如图

  然后开启一个事务,插入两条数据,并提交。假设现在的全局事务id为1,

  新插入数据的 行版本号为当前事务的id,删除版本号为null。

 

  删除

  【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第3张图片

  再次开启一个事务,删除id为2的数据,并提交。假定此时的全局事务id为22,

  删除的数据 的删除版本号修改为当前的事务id

 

  更新

  【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第4张图片

  再次开启事务(开不开的吧,反正都会自动开启并提交),将id为1的数据的name修改为19

  假定此时的事务id已经到了33

  那么他会先将这条数据copy一份,修改原始数据的 删除版本号 为当前的事务id

  新的数据的 行版本号 为当前的事务id,并修改对应的数据

 

  查询

  【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第5张图片

  假定当前的事务id已经到了44。

  然后在其查询的时候是存在一些查询规则的:

  (如果感觉这个规则不太明白的,可以在重新过一遍上面的增删改;

   如果感觉已经明白了的,可以继续向下看案例,可能会让你又不明白了。)

  1、查找数据行版本号等于或早于当前事务版本号的数据行

   也就是说,在我查询的时候,这条数据已经存在了,或者是在我本事务中select之前的数据修改

  2、查找删除版本号要么为null,要么大于当前事务版本号的记录。

   也就是说,在我开启事务并查询的时候,这条数据还没有被删除。

  MVCC案例分析

  但其实呢,这样的MVCC,还是有一定的问题的。

  我们从下面的两个案例来分析一波:

  首先准备两条数据,然后两个事务并发执行

  1234,分别表示两个事务中的开始和执行查询的语句,在两个案例中均未执行提交操作

  【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第6张图片

  案例一(1,2,3,4,2)

   【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第7张图片

   1、先插入就两条数据,行版本号 = 事务id=1

   2、开启一个事务,并执行查询,事务id=2时查询到了两条数据,

   3、开启一个事务,并执行修改操作(这个动作不明白的参考上面的规则),此时的事务id=3

   4、再次执行事务id=2中的查询操作,查询到的数据还是原来的数据

    1)查找数据行版本号等于或早于当前事务版本号的数据行,抛弃表格中的 第三条数据。

    2)查找删除版本号要么为null,要么大于当前事务版本号的记录。两条数据都符合,不变,得到两条数据

  通过第一个案例,我们发现,MVCC的规则,没毛病。

 

  案例二(3,4,1,2,)

   【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第8张图片

   1、先插入就两条数据,行版本号 = 事务id=1

   2、开启一个事务,执行修改操作,此时的事务id=2

   3、开启一个事务,执行查询操作,此时的事务id=3

    1)查找数据行版本号等于或早于当前事务版本号的数据行,全部保留

    2)查找删除版本号要么为null,要么大于当前事务版本号的记录。抛弃第一个数据,得到后两条数据。

 

   但,此时就有了些问题。

   因为此时咱们两个事务是并发执行的,都还没有进行提交操作,我第二个事务读到了第一个事务的操作,这显然非常不合理。

  

  undo、redo、快照读、当前读

  先来抛一些概念,能理解的最好,不理解的可以结合下面的图来进一步吸收一下

  

  Undo Log

  undo log 是在事务开始前,在操作任何数据之前,首先将需要操作的数据备份到一个地方(Undo Log)

 

  实际上,undo log是为了实现事务的原子性而出现的一个产物。

   事务在执行的过程中,如果出现了错误或者用户执行rollback语句时,mysql可以利用undo log中备份的信息将数据恢复到事务开始之前的状态。

 

  在innodb引擎中,undo log被借用来实现 多版本并发控制

   事务未提交前,undo保存了未提交之前的版本数据,undo中的数据可作为数据旧版本快照供其他并发事务进行快照读。

 

  Redo Log

  redo log 是在事务中操作的任何数据,将最新的数据备份到一个地方(Redo Log)

  redo log的持久

   redo log的持久不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo中。具体的落盘策略可以进行配置

  

  redo log是为了实现事务的持久性而出现的产物

   防止在发生故障的时间点,尚有赃页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的未入磁盘数据进行持久化这一特性

 

  快照读

   快照读的 sql读取的数据就是快照版本,也就是历史版本,普通的select就是快照读

   innodb快照读,数据的读取将由cache(原本数据)+ undo(事务修改过的数据)两部分组成

 

  当前读

   当前读的 sql读取的数据是最新版本。通过锁机制来保证读取数据无法通过其他事务进行修改

   UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE 都是当前读

  

  下面咱们来看图吸收一下上面的东西。

【数据库】—— Mysql实现事务隔离级别的方式 — MVCC_第9张图片

  当我们需要对某一个表进行操作的时候,会先将teacher.ibd的数据加载到内存(cache)中。

  当我们开启一个事务之后,会将当前事务涉及到的数据,备份到undo buffer中,如果需要回滚的时候,会通过buffer恢复到teacher中。

 

结语

  Hello World

你可能感兴趣的:(MySQL)