Skip to content

一、什么是MVCC?

MySQL中的MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种提高数据库并发性能的机制,主要用于解决读写冲突,实现非阻塞的读操作。它是InnoDB存储引擎实现事务隔离级别(尤其是REPEATABLE READ和READ COMMITTED)的核心技术。

二、MVCC主要用途

MVCC 通过保存数据的多个版本,让读操作可以读取历史版本的数据。

1.解决并发冲突

  • 读写不阻塞:读操作读取快照数据,不影响写操作更新当前数据。
  • 写写冲突:通过行锁(Record Lock)+ 间隙锁(Gap Lock)处理。

2.提高并发性能

  • 避免了大量的锁竞争
  • 非阻塞的读操作特别适合读多写少的场景。

3.支持事务隔离级别

不同隔离级别下提供不同的一致性保证。

  • READ COMMITTED:每次读取都生成新的ReadView
  • REPEATABLE READ:第一次读取时生成ReadView,整个事务期间复用
    注:MySQL的RR级别通过MVCC+Next-Key Lock基本解决了幻读问题

三、MVCC底层原理(InnoDB引擎)

MySQL的MVCC通过隐藏字段Undo Log版本链ReadView的巧妙组合,实现了高效的非锁定读。
InnoDB 通过以下技术实现 MVCC:

1.隐藏字段

InnoDB为每行记录添加了三个隐藏字段:

  • DB_TRX_ID:6字节,最后一次修改该行的事务ID。
  • DB_ROLL_PTR:7字节,回滚指针,指向该行的上一个版本(存储在undo log中)。
  • DB_ROW_ID:6字节,隐含的行字增ID(当没有主键时自动生成)。

2.Undo Log(回滚日志)

  • 存储数据的历史版本(通过DB_ROLL_PTR形成版本链)。
  • 用于事务回滚、一致性读、崩溃恢复。

3.Read View(读视图)

  • 事务在执行快照读(如SELECT)时,会生成一个Read View,用于判断当前事务能看到哪个版本的数据。
  • Read View 包含:
    • creator_trx_id:创建该Read View的事务ID。
    • trx_ids:未提交事务的id列表。生成ReadView时活跃的事务ID列表,即当前活跃(未提交)的事务ID列表。
    • min_trx_id:活跃事务(trx_ids)中最小的事务ID。
    • max_trx_id:系统将分配给下一个事务的ID,即read view生成时刻系统尚未分配的下一个事务ID。

四、MVCC工作流程

1. 数据版本链的构建

java
版本链示例:
当前行记录 ← undo log v3 ← undo log v2 ← undo log v1
    ↑              ↑             ↑             ↑
 事务T4         事务T3        事务T2        事务T1
(当前版本)    (通过roll_ptr) (通过roll_ptr) (初始版本)

2.数据读取时的版本可见性判断

当执行SELECT时,对于每一行数据,会遍历数据行的版本链(通过DB_ROLL_PTR),按以下规则顺序判断是否可见:

  • 1.如果版本的事务ID等于read_view.creator_trx_id,说明是自己修改的,可见。
  • 2.如果版本的事务ID小于read_view.min_trx_id,说明该版本已提交(事务在ReadView创建前已提交),可见。
  • 3.如果版本的事务ID 大于等于read_view.max_trx_id,说明该版本由未来事务创建(事务在ReadView创建后开始),不可见。
  • 4.如果版本的事务ID在 trx_ids 中,说明该版本由未提交事务创建(事务在ReadView创建时仍活跃),不可见。
  • 5.如果版本的事务ID在 min_trx_id 和 max_trx_id 之间,且不在活跃事务列表中,说明已提交,可见。

3.不同隔离级别的实现差异

  • READ COMMITTED:每次SELECT都会生成新的Read View,能看到其他事务已提交的最新数据。不能解决幻读。
  • REPEATABLE READ:第一次SELECT时生成Read View,后续读取都使用该View,保证可重复读。基本解决幻读。

五、MVCC与锁的协同工作

1. 当前读 vs 快照读

快照读: 不像加锁的select操作就是快照读,即不加锁的非阻塞读。快照读的前提是隔离级别不是串行化,串行化级别下快照读退化成当前读。快照读的实现是基于mvcc。 当前读:

  • 1.select xx lock in share mode(共享锁);
  • 2.select xx for update、insert、update、delete (排他锁);
    以上这些操作都是一种当前读,即它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
sql
-- 快照读(使用MVCC)
SELECT * FROM table;  -- 普通SELECT

-- 当前读(使用锁)
SELECT * FROM table LOCK IN SHARE MODE;  -- 加共享锁
SELECT * FROM table FOR UPDATE;          -- 加排他锁
UPDATE/DELETE/INSERT;                    -- 总是当前读

2. Next-Key Lock防止幻读

在RR级别下,MVCC配合Next-Key Lock:

  • 快照读:通过MVCC避免幻读。
  • 当前读:通过Next-Key Lock(记录锁+间隙锁)避免幻读。

六、MVCC的优缺点

优点

  • 高并发:读写不相互阻塞。
  • 避免死锁:读操作不需要加锁。
  • 一致性读:提供时间点的一致性快照。
  • 避免幻读(在RR级别下通过MVCC + Next-Key Lock解决)。
  • 回滚高效:直接通过undo log回滚到历史版本。
    缺点
  • 额外存储:需要维护undo log和版本链。
  • 清理开销:依赖后台purge线程清理不再需要的undo log。
  • 历史数据:长时间事务可能阻止旧版本清理。

Released under the MIT License.