MVCC

523 阅读3分钟

一、什么是MVCC

    MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。

二、为什么需要MVCC

   先了解数据库的并发厂家,常见的并发场景有三种,分别为:

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
  • 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

      在MySQL 的InnoDB引擎中,主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

      先看几条记录,这是我们数据库的一条静态纯洁的数据,(后面的三列,是数据库的隐藏数据,每条记录都会有的)。为什么说它静态纯洁,是因为它最后两列都是null,那最后两列是什么意思呢。DB_TRX_ID是事务ID,显示的是现在谁在操作这条数据,为null的意思是,当前没有事务在操作这条数据,是一条“安全、静态”数据,最后一列DB_ROLL_PTR是回滚指针的地址,也是null,证明这条数据没有被修改过,只被初始插入过一次后来没有被修改过。

     于此相对应的下面这条就是非静态不安全的数据了,DB_TRX_ID显示它正在被1事务操作,DB_ROLL_PTR回滚指针不为空证明它之前被修改过你,0x12446545显示的就是修改记录的地址。

接下来,MVCC它怎么帮我们解决读写冲突呢?

先介绍一个新成员--undo日志

那么问题来了什么是undo日志,为什么需要undo日志。先上两张图自己感受一下。

 第一张是对数据修改一次之后生成的undo日志:

   第二张是对数据修改两次之后生成的undo日志:

     从上面,我们就可以看出,不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线性表,既链表,undo log的链首就是最新的旧记录,链尾就是最早的旧记录,这就解释了undo日志是什么的问题。

      那我们为什么需要undo日志呢,undo日志能干什么呢,这就需要讲下MVCC的作用了。上面说了MVCC为了解决读-写冲突,现在假设有两个事务对同一行数据进行操作,事务一是读事务,事务二是写事务。事务二先执行,想把Tom改成25岁,这个时候就存在并发冲突了,事务二写入操作:

事务一读取操作:

MVCC就站出来说,全部不要动,让我照一张照片(这张照片就是Read View(读视图))记录这一刻,然后用这张照片去分析这一刻的复杂情况。

    Read View不仅仅会通过一个列表trx_list来维护事务一执行快照读那刻系统正活跃的事务ID,还会有两个属性up_limit_id(记录trx_list列表中事务ID最小的ID),low_limit_id(记录trx_list列表中事务ID最大的ID,

      所以先拿该记录DB_TRX_ID字段记录的事务ID 1去跟Read View的的up_limit_id比较,看1是否小于up_limit_id(1),所以不符合条件,继续判断 1是否大于等于 low_limit_id(2),也不符合条件,最后判断1是否处于trx_list中的活跃事务, 最后发现事务ID为1的事务在当前活跃事务列表中,  然后就去undo日志找到1事务。(这个地方其实也很简单,就是去比较版本号,只能查看比你小的版本,不能查看比你大的版本ID)