多版本并发控制MVCC

 费德  2016/08/18 15:21  781 次

在数据库中分为四种隔离:

  1. 未提交读:脏读

  2. 提交读:不可重复读

  3. 可重复读:幻读

  4. 串行化:性能差

大多数数据库系统默认采用的是第二个隔离级别,而mysql使用的是第三级隔离,但是它会造成幻读的问题。第四个隔离级别虽然可以解决这个问题,但是会造成性能的急剧下降。因而,我们采用另外一种方法来解决这个问题:多版本并发控制(MVCC).
我们先看下MVCC的简单解释:

MVCC的原理与copyonwrite类似,全称是Multi-Version Concurrent
Control,即多版本并发控制。在MVCC协议下,每个读操作会看到一个一致性的snapshot,并且可以实现非阻塞的读。MVCC允许数据具有多个版本,这个版本可以是时间戳或者是全局递增的事务ID,在同一个时间点,不同的事务看到的数据是不同的。

MVCC的关键在于版本号或者时间戳。
InnoDB中MVCC实现原理如下:
实现原理:

------------------------------------------------------------------------------------------> 时间轴
        |-------R(T1)-----|
    |-----------U(T2)-----------|

如上图,假设有两个并发操作R(T1)和U(T2),T1和T2是事务ID,T1小于T2,系统中包含数据a = 1(T1),R和W的操作如下:
R:read a (T1)
U:a = 2 (T2)
R(读操作)的版本T1表示要读取数据的版本,而之后写操作才会更新版本,读操作不会。在时间轴上,R晚于U,而由于U在R开始之后提交,所以对于R是不可见的。所以,R只会读取T1版本的数据,即a = 1。
由于在update操作提交之前,不能影响已有数据的一致性,所以不会改变旧的数据,update操作会被拆分成insert + delete。需要标记删除旧的数据,insert新的数据。只有update提交之后,才会影响后续的读操作。而对于读操作而且,只能读到在其之前的所有的写操作,正在执行中的写操作对其是不可见的。
上面说了一堆的虚的理论,下面来点干活,看一下mysql的innodb引擎是如何实现MVCC的。innodb会为每一行添加两个字段,分别表示该行创建的版本和删除的版本,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别(事务的隔离级别请看这篇文章)下,具体各种数据库操作的实现:

  • select:满足以下两个条件innodb会返回该行数据:(1)该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。(2)该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。

  • insert:将新插入的行的创建版本号设置为当前系统的版本号。

  • delete:将要删除的行的删除版本号设置为当前系统的版本号。

  • update:不执行原地update,而是转换成insert

  • delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。
    其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。

由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。
通过MVCC很好的实现了事务的隔离性,可以达到repeated read级别,要实现serializable还必须加锁。

 作者:费德

少年费德的奇幻漂流

本博客如无特殊说明皆为原创,转载请注明来源:多版本并发控制MVCC

添加新评论