Mysql 多版本并发控制(MVCC)

294 阅读6分钟

我们知道事务有四大特性原子性,一致性,隔离性,持久性。今天我们就来看看其中的隔离性以及Mysql中多版本并发控制(MVCC)和隔离性的关系。

事务的隔离性

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。(可能脏读、不可重复读、可能幻读)

  • 读提交: 一个事务提交之后,它做的变更才会被其他事务看到。(不可重复读、可能幻读)

  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。即一个事务中多次查询某个记录,看到的一定是一致的。(可能幻读)

  • 串行化: 顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

注意:Mysql 默认隔离级别是可重复读。且InnoDB的可重复读级别通过间隙锁解决了幻读问题。

多版本并发控制

简单的说,就是不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读。

说明:多版本并发控制只在读提交和可重复读隔离级别下生效。

优势:

  • 读不加锁,读写不冲突。

  • 在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。

下图形象的说明了什么是多版本

数据库中某行数据,根据不同的事务启动时刻不同,该行记录有多个版本,如上图就会有V1, V2, V3, V4四个版本。那这些版本是怎么实现的,是每次更新都写一个副本吗?接下来看另外一张图。

结合我们之前文章中提到的,数据库更新语句详细流程,有提到每次更新会把老数据存到undo log页,上面这个图就形象的说名当tx2再次做更新时,多记录了一条undo log 101到undo log日志中,比如需要V2版本的信息时,通过当前行的最新数据再根据undo log信息,逻辑回滚到对应的V3,再回滚到V2就好了。

另外你可能会问undo log不能一直存在吧,什么时候删除呢?答案是InnoDB会自己判断系统中没有事务可以对其可见了,具体往下看就知道了。

下面这个图形象的说明,InnoDB代码中如何通过事务控制版本的可见性。

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。
在可重复读隔离级别下每个事务开始时,申请一个事务ID,开启一个一致性读视图。相当于事务启动时拍了一个快照(在读提交隔离级别下每条语句就会开启一个事务)。
重点来了,InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前系统以及启动但未提交的所有事务 ID。
在当前事务中查询时,只查低水位以下的和高水位以下且不在数组中的数据。

下面我们以一张图解释下这个过程,强调下,下面这个分析在可重复读隔离级别下

我们假设刚开始的时候,某行记录(id, k)是(1,1)是事务ID为90的那次更新的。系统慢慢的又有新的事务申请了。

  1. 事务A开启,申请的事务ID是100,这个时候获取系统中所有是事务ID,假设有99,100,所以事务A维护的事务数组是[99, 100],那根据我们上面说的规则它只能看到99以下更新的数据,和本身的事务100,这时正准备查记录
  2. 事务B开启了,申请的事务ID是101,事务B维护的事务数组就是[99, 100, 101], 正准备更新字段值k = k + 1
  3. 事务C开启了,申请的事务ID是102,事务C维护的事务数组是[99, 100, 101, 102], 事务C执行的快,先执行了k = k + 1,那改行记录就变成了,由102号事务更新的最新数据(1,2)
  4. 事务C执行完commit后,事务B开始执行了,(注意这时是更新操作,属于当前读,读最新数据,并不套用多版本并发控制规则哦),102号事务更新的数据(1,2)变成了历史版本。当前版本变成由101号事务更新的最新数据(1,3)。
  5. 假设事务B执行完并提交了(当然提不提交不影响我们的结果),事务A正式开始查询改行记录,套用多版本并发控制,我们应该得到的值是多少?对的,就是(1,1)

为什么是(1, 1)?
因为根据上面的规则虽然102事务,101事务都已经提交了。但根据规则,事务A开启时申请的事务ID是100,我们在步骤1中就已经说了,由于多版本并发控制的规则,事务A只能看到99以下的事务更新的数据,以及自己本身事务100更新数据,根据这个规则,InnoDB在查询到改行记录(1,3)时,更加undo log日志回滚到(1,2)发现是102号事务更新的,继续回滚回滚到(1,1)发现是90号事务更新的,可见,返回(1,1)。

上面就是我们介绍多版本并发控制的全部内容了,由此可见undo log只有在系统中不存在未提交事务,可能对其可见时,才会清除掉,对undo log回收很不友好,另外长事务还占用锁资源,也会对系统造成很大影响,我们一定要避免长事务哦。