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