MySQL的MVCC是什么,有什么用?

一、介绍

面试被问到了MVCC,我不知道啊,一脸懵逼!

于是回家查询了资料,记录一下

实际上,MVCC的全称是Multi Version ConCurrency Control,翻译过来就是多版本并发控制。

二、概念

1)隔离级别

指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行。在MySQL中,这样大幅度提高了InnoDB的并发度。在内部实现中,InnoDB通过undo log保存每条数据的多个版本,并且能够找回数据历史版本提供给用户读,每个事务读到的数据版本可能是不一样的。在同一个事务中,用户只能看到该事务创建快照之前已经提交的修改和该事务本身做的修改。

首先我们先了解一下数据库事务的隔离级别

  • 未提交读(READ UNCOMMITED):也就是脏读,当一个事务读取到另外一个事务修改但未提交的数据时

  • 已提交读 (READ COMMITED):简称RC

  • 可重复读(REPEATABLE READ):简称RR

  • 可串行化(SERIALIZABLE)

隔离级别 脏读 不可重复读 幻读
未提交读 可能 可能 可能
已提交读 不可能 可能 可能
可重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能

那么先介绍下面的现象

  • 脏读:当一个事务读取到另外一个事务修改但未提交的数据时

  • 不可重复读:在同一个事务中,同样的条件SQL查询出的结果不一致

  • 幻读:在同一个事务中,同样的条件SQL查询出的结果不一致==(重点在于,数据有新增或者删除,导致的结果不一致)==

2)undo log

在上面有提到undo log,那么这个undo log是什么呢?

实际上是当多个事务操作一条数据时,每个事务中的每个操作都会产生一条记录,比如说下面这样

image-20230909130137655

可以看到,当一个事务TX111修改了数据值,就会产生一条undo log,并记录指向上一条最原始的这条undo log


如果有多条事务,修改同一条数据,那么就会产生链表一样的结构,我们称为undo log版本链表,如下

image-20230909130109990

undo log,会进行删除,但不是立即删除。

它会在确保,当前undo log不被引用后,再进行删除。也就是当事务所有完成后,也就是commitrollback,保留最终确认下的undo log,并删除之前所有的版本链。

3)快照读、当前读

好的,现在要介绍一下快照读当前读,只要介绍了这个,我们就能了解MVCC到底是什么了

  • 快照读readView):当执行查询select语句时,提取数据的一个记录

  • 当前读:当执行下面的语句时,提取数据的一个记录

    • insertupdatedeleteselect...for updateselect...lock in share mode

上面了解到他们是一个数据记录,那么其中他们有什么数据呢

字段 说明
m_ids 当前活跃的事务编号集合,也就是还没有被提交回滚的事务集合
min_trx_id 最小的活跃事务编号
max_trx_id 预分配事务编号,当前最大事务编号+1
creator_trx_id 快照读创建者的事务编号

好的,我们来进行理解一下这个快照读

image-20230909125747658

4)多版本并发控制流程

上面的概念都看完了,接下来可以讲讲MVCC了,他是怎么使用RRRC来影响事务读取的数据的呢?

快照读配合当前读会影响,读取的结果,我们看下面的undo logreadView

image-20230909130447845

我们要确定版本时,就是拿着快照读去匹配版本链上的每一个undo log,从最后往前进行判断

使用这些判断条件,MySQL就能确定要读取的版本了

  1. 判断undo logtrx_id == creator_trx_id

    1. 相等,则说明这条undo log修改,就是本事务自己更新修改的。可以访问
  2. 判断undo logtrx_id < min_trx_id

    1. 成立,则说明当前判断的这个undo log已经提交。可以访问
  3. 判断undo logtrx_id >= max_trx_id

    1. 成立,则说明当前判断的这个undo log,是在产生快照读之后创建的事务。所以不允许访问
  4. 判断min_trx_id <= trx_id < max_trx_id,成立则继续判断,trx_id是否存在于m_ids里面

    1. 成立,则说明undo log的事务还没有提交。不允许访问
    2. 不成立,则说明undo log的事务已经提交。允许访问

根据上面的判断条件,我们来进行判断

  • 首先是第一个undo log

    1. trx_id == creator_trx_id,即TX222 == TX333。不成立,继续下一个判断条件
    2. trx_id < min_trx_id,即TX222 < TX222。不成立,则继续下一个判断条件
    3. trx_id >= max_trx_id,即TX222 >= TX334。不成立,则继续向下判断
    4. min_trx_id <= trx_id < max_trx_id,即TX222 <= TX222 <= TX334。成立,则还要判断是否处于活跃事务集合中
      1. TX222处于集合[TX222, TX333]之中,不允许访问
  • 上面的条件都不满足后,我们将要继续下一个undolog

    1. trx_id == creator_trx_id,即TX111 == TX333。不成立,继续下一个判断条件
    2. trx_id < min_trx_id,即TX111 < TX222
      1. 成立,则说明TX111的事务已经提交,允许访问,确定下来一个数据的访问

按照上面的方法,进行判断

image-20230909134721632

会读取到这样的一个结果,注意看,在TX333的事务下,三次读取出现了三次不同的结果,这便是不可重复读

那么MVCC该如何控制呢,其实问题主要的发生原因,是每次的readView不一致导致的,既然快照读不一致,所查询的结果肯定也不一致。

那么只需要,将同一个事务中快照读,变成同一份,不就是可重复读的隔离级别了嘛。

故事务中,将第一次快照读进行保存,后面的读取都按照这份快照读进行复用。

image-20230909135858466


那么为什么说可重复读RR,并不能完全解决幻读的问题呢?

因为,在同一个事务中,快照读是复用的,一旦事务中出现了一次当前读,也就是执行了update等语句,那么就会重新刷新快照读。一旦快照读发生了改变,幻读就有可能出现了。

image-20230909141251609

不可重复读,是指一个事务中,两次读取的结果不一致的现象。

同一个事务中,如果是因为自己修改了数据,从而导致两次查询结果不一致的情况,这是正常现象,不叫不可重复读

这也正是,为什么发生当前读后,快照读要重新进行生成的原因。因为要读取到自己事务上一刻修改的数据。

三、最后

好了,我是被面试问到的,真的是一脸懵逼,服了

我是半月,你我一同共勉!!!