原创声明

本文整理自:


什么是 MVCC

MVCC(Multi-Version Concurrency Control)即多版本并发控制,是现代数据库常用的处理读写冲突的方式,目的是提高数据库在高并发场景下的吞吐能力

通过 MVCC 机制,SELECT 操作可以在不加锁的情况下读取指定版本的历史记录,并且可以保证读取到的记录值符合事务所处的隔离级别,从而解决并发场景下的读写冲突。


InnoDB 如何实现 MVCC

InnoDB 会为每行记录增加两个隐藏列:DATA_TRX_ID、DATA_ROLL_PTR。(如果表没有主键,还会增加一个隐藏的主键列 DB_ROW_ID)

hidden-fields.jpg

在多个事务并行操作某行记录时,不同事务对该行记录的 UPDATE 操作会导致该行记录产生多个历史版本,Innodb 通过回滚指针将它们组织成一个 Undo Log 链。在下面的例子中,插入该行的事务的 ID 是 100,隐藏主键是 1,事务 A 的 ID 是 200,事务 A 更新值 x 后,该行产生一个新版本和一个旧版本。

undolog-chain.jpg

事务 A 的操作过程是:

相比 UPDATE 操作,INSERT 和 DELETE 操作比较简单。INSERT 会产生一行新记录,它的 DATA_TRX_ID 是插入记录的事务的 ID。DELETE 其实是软删,在 commit 时,才会真正地执行删除操作,DATA_TRX_ID 会记录删除该行的事务的 ID。


如何实现一致性读 - ReadView

对于 READ UNCOMMITTED 隔离级别,直接读取记录的最新版本即可;对于 SERIALIZABLE 隔离级别,可以通过加锁来互斥地访问数据,因此对于这两个隔离级别而言,不需要 MVCC 的帮助。MVCC 运行在 READ COMITTED 和 REPEATABLE READ 隔离级别下,如果事务的隔离级别被设置为两者中的一个,那么在 SELECT 数据时,就会用到版本链。

为了解决版本链中的哪些版本对当前事务可见的问题,InnoDB 引入了 ReadView 的概念。

ReadView 是当前活跃的事务 ID 列表,称之为 m_ids,其中的最小值被称为 up_limit_id,最大值被称为 low_limit_id。事务 ID 是事务开启时,由 InnoDB 分配的,其大小表明了事务开启的先后顺序。因此,InnoDB 通过事务 ID 的大小关系决定版本的可见性


RR 和 RC 隔离级别下 ReadView 的生成

在 RR 隔离级别下,每个事务 touch first read 时(即第一次执行 SELECT 操作时),会将系统中的所有活跃的事务的 ID 拷贝到一个列表中,生成 ReadView,之后所有读操作都会复用该 ReadView。

在 RC 隔离级别下,每次执行 SELECT 操作时,都重新将系统中的所有活跃的事务的 ID 拷贝到一个列表中,生成 ReadView。

可见二者的区别是生成 ReadView 的时间点不同,前者是在事务第一次执行 SELECT 操作时生成,后者是在事务每次执行 SELECT 操作时都生成。