MySQL 的 **MVCC(Multi-Version Concurrency Control,多版本并发控制)**是一种实现数据库高并发访问的核心机制,主要用于解决读写冲突,避免事务之间的阻塞,同时保证不同隔离级别下的数据一致性(如READ COMMITTED
和 REPEATABLE READ
)。以下是其核心原理和实现细节:
一、MVCC 的核心思想
- 数据多版本:每个数据行在修改时,不会直接覆盖原数据,而是生成一个历史版本链。
- 快照读:事务在读取数据时,只能看到在它启动时已经提交的版本,或自身修改的版本,其他未提交的修改不可见。
- 无锁读:读操作不阻塞写操作,写操作也不阻塞读操作(仅针对快照读)。
二、MVCC 的实现依赖
1. 隐藏字段
InnoDB 每行数据包含三个隐藏字段:
• DB_TRX_ID
(6字节):最近修改该行的事务ID。
• DB_ROLL_PTR
(7字节):回滚指针,指向 undo log 中的旧版本数据。
• DB_ROW_ID
(6字节):隐含的自增行ID(若表无主键)。
2. Undo Log(回滚日志)
• 存储数据修改前的历史版本,用于构建多版本链。
• 事务对数据修改时,会生成对应的 undo log:
• INSERT 操作生成 undo log,用于事务回滚时删除数据。
• UPDATE/DELETE 操作生成 undo log,用于构建多版本链。
3. Read View(读视图)
事务在读取数据时生成的一致性视图,决定哪些版本的数据可见。其关键属性包括:
• trx_ids
:生成 Read View 时活跃的事务ID列表(未提交的事务)。
• min_trx_id
:trx_ids
中的最小事务ID。
• max_trx_id
:下一个将分配的事务ID(当前最大事务ID +1)。
• creator_trx_id
:创建该 Read View 的事务ID(当前事务的ID)。
三、MVCC 的工作流程
1. 数据版本链
每条数据通过 DB_ROLL_PTR
指向 undo log 中的旧版本,形成一个链表结构:
当前行数据(版本N) → undo log(版本N-1) → undo log(版本N-2) → ... → 初始版本
2. 可见性判断规则
事务通过 Read View 判断某数据版本是否可见:
- 版本的事务ID <
min_trx_id
:该版本已提交,可见。 - 版本的事务ID ≥
max_trx_id
:该版本由未来事务生成,不可见。 - 版本的事务ID 在
trx_ids
中:该版本由未提交事务生成,不可见。 - 版本的事务ID =
creator_trx_id
:该版本由当前事务自己生成,可见。 - 其他情况:需遍历 undo log 链,找到最近的可见版本。
四、不同隔离级别的实现
1. READ COMMITTED(读已提交)
• Read View 生成时机:每次执行 SELECT 语句时生成新的 Read View。
• 效果:能看到其他事务已提交的最新数据,但可能发生“不可重复读”。
2. REPEATABLE READ(可重复读,MySQL 默认级别)
• Read View 生成时机:事务第一次执行 SELECT 时生成 Read View,后续复用。
• 效果:整个事务中看到的数据版本一致,避免不可重复读和部分幻读(通过 MVCC + Next-Key Lock 解决)。
五、MVCC 的写操作处理
• UPDATE/DELETE:基于当前最新数据版本进行修改,生成新的 undo log。
• INSERT:新插入的数据对其他事务不可见,直到当前事务提交。
六、MVCC 的优缺点
优点
• 读写不冲突,提高并发性能。
• 避免锁竞争,减少死锁概率。
• 支持快照读,适合读多写少场景。
缺点
• 需要维护多个数据版本,占用额外存储空间。
• 频繁更新可能导致 undo log 链过长,影响查询性能。
• 需要定期清理旧版本数据(Purge 机制)。