mvcc的核心是基于版本链的数据可见性控制机制,而非编程意义上的链表变量;它通过隐藏字段(db_trx_id、db_roll_ptr)与undo log逻辑构建单向版本链,并结合read view判断事务可见性,实现快照读、避免读写阻塞。

MVCC 的核心不是“链表变量”,而是基于版本链的数据可见性控制机制。数据库里并不存在编程语言中那种可直接赋值、遍历的链表变量(如 LinkedList<t></t>),所谓“版本链”是 InnoDB 通过隐藏字段和 Undo Log 逻辑组织起来的历史数据结构,本质是一条由回滚指针串起的单向链表,仅在事务读取判断时被内部遍历,不对用户暴露。
下面从实际作用出发,说清楚它在事务中怎么用、为什么重要:
版本链是怎么形成的
每行记录背后有三个关键隐藏字段:
-
DB_TRX_ID:最后一次修改该行的事务 ID -
DB_ROLL_PTR:指向 Undo Log 中前一版本的指针(即“链表节点”的连接方式) -
DB_ROW_ID:行唯一标识(非核心,可忽略)
当事务执行 UPDATE 或 DELETE:
- 原始数据不覆盖,而是把旧值写入 Undo Log
- 新记录的
DB_TRX_ID设为当前事务 ID,DB_ROLL_PTR指向刚写入的 Undo Log 位置 - 这样就自然形成一条从最新版 → 上一版 → 更早版的单向链
例如:
balance = 1000(事务99创建)
↓ DB_ROLL_PTR
balance = 800(事务101修改)
↓ DB_ROLL_PTR
balance = 500(事务102修改,尚未提交)
Read View 如何用这条链做可见性判断
事务执行快照读(普通 SELECT)时,InnoDB 生成一个 Read View,包含:
-
m_ids:当前活跃未提交事务 ID 列表 -
min_trx_id:m_ids中最小值 -
max_trx_id:下一个待分配事务 ID -
creator_trx_id:本事务自己的 ID
然后沿着版本链从新到旧扫描,对每个版本按规则判断是否可见:
- 若版本
DB_TRX_ID → 属于已提交的“过去”,<strong>可见</strong> - 若版本
DB_TRX_ID == creator_trx_id→ 本事务自己改的,可见 - 若版本
DB_TRX_ID ∈ m_ids→ 属于其他未提交事务,不可见 - 其他情况(如 ≥
max_trx_id)→ 视为“未来”版本,不可见
直到找到第一个可见版本,就返回它——这就是你看到的“一致性快照”。
不同隔离级别下,链的使用方式不同
-
读已提交(RC):每次
SELECT都生成新 Read View,所以同一事务内两次查询可能看到不同版本(因为中间有别的事务提交了) -
可重复读(RR):只在第一次
SELECT时生成 Read View,后续查询复用它,因此能保证“可重复读”——哪怕别的事务已提交新版本,链上更老但符合 Read View 规则的版本仍被选中
注意:链不是万能的,也有边界
- 它解决不了幻读(新插入的行不在原版本链上,需配合间隙锁)
- Undo Log 不清理会导致版本链过长、空间膨胀,影响性能和 purge 效率
-
DELETE后的记录虽逻辑删除,仍保留在链中,直到所有依赖它的事务结束
本质上,版本链是 MVCC 的物理载体,Read View 是它的决策规则。两者配合,让数据库在不加锁的前提下,给每个事务发一张“定制快照”,既并发又一致。










