介绍
MVCC (多版本并发控制) 是数据库(尤其是 MySQL InnoDB、Oracle、PostgreSQL)实现高并发事务的核心无锁机制 。它通过为每行数据维护多个历史版本,实现 读写互不阻塞,在保证隔离性(ACID 中的 I)的同时大幅提升并发性能。
作用
传统锁机制(悲观锁):
读加共享锁 (S),写加排他锁 (X)
读阻塞写、写阻塞读,高并发下性能瓶颈
MVCC 优势:
读不加锁(快照读),写不阻塞读,读不阻塞写
事务看到的是一致性数据快照,而非实时最新值
完美解决 脏读 、不可重复读 ,配合间隙锁可解决幻读
原理
InnoDB 通过 隐藏字段 + Undo Log + ReadView 实现 MVCC。
- 行记录隐藏字段
每行数据额外隐式存储 3 个字段(用户不可见):
DB_TRX_ID (6 字节):创建 / 最后修改该行的事务 ID(全局递增)
DB_ROLL_PTR (7 字节):回滚指针,指向 Undo Log 中的上一个历史版本
DB_ROW_ID (6 字节):隐藏主键(表无主键时自动生成)

- Undo Log(回滚日志)
作用:存储数据修改前的旧版本快照,用于事务回滚 & MVCC 历史读取
类型:
INSERT Undo Log:事务提交后立即删除
UPDATE/DELETE Undo Log:提交后暂存,供快照读读取历史版本,后台定时清理
版本链:
多次 UPDATE/DELETE 时,通过 DB_ROLL_PTR 把所有历史版本串成单向链表(最新版本在表中,旧版本在 Undo Log 里)。

- ReadView(读视图 / 一致性快照)
ReadView 是判断 "哪个版本可见" 的核心规则,在快照读(普通 SELECT)时生成。
ReadView 四要素:
m_ids:生成快照时,所有活跃未提交事务 ID 列表
min_trx_id:m_ids 中最小事务 ID
max_trx_id:系统下一个待分配的事务 ID(当前最大 ID + 1)
creator_trx_id:当前事务自己的 ID
版本可见性判断算法
沿版本链从最新到最旧遍历,用 DB_TRX_ID 与 ReadView 比对,第一个满足以下任一条件即可见:
自己修改的: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 → 已提交 → 可见
若 trx_id 在 m_ids → 仍活跃未提交 → 不可见
隔离级别与 ReadView 生成时机
MVCC 只在 RC 和 RR 级别生效;RU(总是读最新)、SERIALIZABLE(加锁读)不使用 MVCC。
- READ COMMITTED (读已提交,RC)
每次 SELECT 都生成新 ReadView
特点:总能读到其他事务已提交的最新值
问题:不可重复读(同事务内两次 SELECT 结果可能不同)
- REPEATABLE READ (可重复读,RR, MySQL 默认)
事务中第一次 SELECT 时生成 ReadView,后续复用
特点:整个事务内数据一致,看不到其他事务后续提交修改
解决:脏读、不可重复读;配合 Next-Key Lock(查表时不能添加数据) 解决幻读

快照读和当前读
快照读(MVCC 生效),普通 SELECT,不加锁,读取历史版本。
当前读(加锁,MVCC 不生效),读取最新版本并加锁,保证数据强一致性。
SELECT ... FOR UPDATE / LOCK IN SHARE MODE
INSERT / UPDATE / DELETE