
MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。它是一种为了提高数据库并发性能而提出的技术,使得在并发读写数据库时,读操作不会阻塞写操作,写操作也不会阻塞读操作。这就解决了传统的锁机制带来的性能瓶颈问题。
MySQL 中,InnoDB 存储引擎 实现了 MVCC。
在没有 MVCC 的情况下,如果我们要保证事务的隔离性(例如可重复读级别),通常会用锁来实现。当一个事务正在读取某些数据时,其他事务就不能修改这些数据(共享锁),这会导致“读-写”冲突;同样,一个事务在修改数据时(排他锁),其他事务也不能读取,这会导致“写-读”冲突。MVCC 通过创建数据的历史版本来优雅地解决这个问题。
核心思想: 为每行数据维护多个历史版本。当一个事务需要读取数据时,它会看到在它开始之前就已经提交的某个一致性数据快照,而不管当前这些数据被其他事务修改成了什么样子。
MVCC 的实现依赖于三个核心组件:
下面我们逐一详解。
InnoDB 为每一行数据(记录)都添加了三个系统隐藏字段:

DB_TRX_ID (6字节):事务ID。表示最后一次插入或更新该行的事务ID。此外,删除在内部也被视为更新,会在该行数据中设置一个特殊的删除标记。DB_ROLL_PTR (7字节):回滚指针。指向该行数据的上一个历史版本,存储在 Undo Log 中。DB_ROW_ID (6字节):行ID。随着新行插入而单调递增的行ID。如果表没有定义主键,InnoDB 会基于这个字段生成一个聚簇索引。注意: 实际上还有一个删除标记的隐藏字段,用于标记该行是否被删除。

Undo Log(回滚日志)主要有两个作用:
工作原理: 当一个事务对某行数据进行修改(INSERT, UPDATE, DELETE)时:
DB_TRX_ID 和 DB_ROLL_PTR。新的 DB_ROLL_PTR 会指向这个刚刚存入 Undo Log 的旧版本。然后才在表中修改该行数据,写入新的 DB_TRX_ID 和新的 DB_ROLL_PTR。因此,通过 DB_ROLL_PTR 指针,一行数据的所有历史版本(快照)被串联成一个链表,这个链表就存放在 Undo Log 中。这个链表称为 版本链。链表的头节点是当前的最新记录。
Read View 是事务在进行快照读操作时产生的。它定义了当前事务在执行期间,能看到哪些版本的数据。
Read View 主要包含以下几个关键属性:
m_ids:生成 Read View 时,系统中活跃的(未提交的)读写事务的事务ID列表。min_trx_id:m_ids 中的最小值。max_trx_id:生成 Read View 时,系统应该分配给下一个事务的ID。(注意:不是 m_ids 的最大值,而是已创建的最大事务ID+1)。creator_trx_id:创建该 Read View 的事务ID。当一个事务执行一条 SELECT 语句(快照读)时,它需要遍历数据行的版本链,并利用自己的 Read View,通过一套算法来决定哪个版本对它来说是可见的。
对于版本链中的某个版本,假设其对应的事务ID为 trx_id,判断规则如下:

trx_id == creator_trx_id:trx_id < min_trx_id:trx_id >= max_trx_id:min_trx_id <= trx_id < max_trx_id:trx_id 是否在 m_ids(活跃事务列表)中:MVCC 主要在 READ COMMITTED 和 REPEATABLE READ 这两个隔离级别下工作。


MVCC 工作流程总结:
DB_TRX_ID 和 DB_ROLL_PTR。判断流程图:

SELECT 语句,基于 MVCC 和 Read View 读取历史版本,不加锁。SELECT 语句(如 SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE)以及 INSERT, UPDATE, DELETE。当前读读取的是记录的最新版本,并且会通过加锁(Next-Key Lock)来保证数据一致性。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。