MVCC 多版本并发控制
Table of Contents

MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。

在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。

在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:

InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏列来实现的。这两个列,一个是创建时间列, 保存行的 insert 时间或者 update 时间,一个是删除时间列, 保存行的 delete 时间。当然这个时间并不是实际的时间值,而是系统版本号(system version number, svn)。每开始一个新的事务,系统版本号都对自动递增。事务开始时刻的系统版本号会作为当前事务的版本号,用来和查询到的每行记录的版本号进行比较。

下面看一下在 REPEATABLE READ 隔离级别下,MVCC 具体是如何操作的。

保存这个额外的系统版本号,使大多数读操作都可以不用加锁, non-blocking reads 就是这么实现的。这样设计使得读操作很简单,性能很好,并且保证只会读取到符合条件的行。不足之处是每行记录都要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作

值得注意的是,在事务隔离级别READCOMMITTED和REPEATABLEREAD(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在READCOMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一 份快照数据。而在REPEATABLEREAD事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。