什么是 MVCC
MVCC (Multiversion Concurrency Control)
中文全程叫多版本并发控制,是现代数据库(包括 MySQL
、Oracle
、PostgreSQL
等)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能。
如此一来,不同事务并发过程中,SELECT
操作可以不加锁而是通过 MVCC
机制读取指定的版本历史记录,并通过一些手段保证保证读取的记录值符合事务所处的隔离级别,从而解决并发场景下的读写冲突。
下面举一个多版本读的例子,例如两个事务 A
和 B
按照如下顺序进行更新和读取操作
在事务 A
提交前后,事务 B
读取到的 x
的值是什么呢?答案是:事务 B
在不同的隔离级别下,读取到的值不一样。
- 如果事务
B
的隔离级别是读未提交(RU),那么两次读取均读取到x
的最新值,即20
。 - 如果事务
B
的隔离级别是读已提交(RC),那么第一次读取到旧值10
,第二次因为事务A
已经提交,则读取到新值 20。 - 如果事务
B
的隔离级别是可重复读或者串行(RR,S),则两次均读到旧值10
,不论事务A
是否已经提交。
可见在不同的隔离级别下,数据库通过 MVCC
和隔离级别,让事务之间并行操作遵循了某种规则,来保证单个事务内前后数据的一致性。
InnoDB 中的 MVCC
本文聚焦于 MySQL
中的 MVCC
实现,从 《高性能 MySQL》
一书中对 MVCC
的介绍可知:
MySQL
中InnoDB
引擎支持MVCC
- 应对高并发事务,
MVCC
比单纯的加行锁更有效, 开销更小 MVCC
在读已提交(Read Committed)
和可重复读(Repeatable Read)
隔离级别下起作用MVCC
既可以基于乐观锁又可以基于悲观锁来实现
InnoDB MVCC 实现原理
InnoDB
中 MVCC
的实现方式为:每一行记录都有两个隐藏列:DATA_TRX_ID
、DATA_ROLL_PTR
(如果没有主键,则还会多一个隐藏的主键列)。
DATA_TRX_ID
记录最近更新这条行记录的事务 ID
,大小为 6
个字节
DATA_ROLL_PTR
表示指向该行回滚段(rollback segment)
的指针,大小为 7
个字节,InnoDB
便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo
中都通过链表的形式组织。
DB_ROW_ID
行标识(隐藏单调自增 ID),大小为 6
字节,如果表没有主键,InnoDB
会自动生成一个隐藏主键,因此会出现这个列。
如何组织 Undo Log 链
关于 Redo Log 和 Undo Log 的相关概念可见之前的文章 InnoDB 中的 redo 和 undo log
上文提到,在多个事务并行操作某行数据的情况下,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针组织成一条 Undo Log
链,这节我们通过一个简单的例子来看一下 Undo Log
链是如何组织的,DATA_TRX_ID
和 DATA_ROLL_PTR
两个参数在其中又起到什么样的作用。
还是以上文 MVCC
的例子,事务 A
对值 x
进行更新之后,该行即产生一个新版本和旧版本。假设之前插入该行的事务 ID
为 100
,事务 A
的 ID
为 200
,该行的隐藏主键为 1
。