ReadView是干嘛的?

它是 InnoDB 实现 MVCC 的核心,用来判断某个行版本,对当前这次一致性读取是否可见

Read View 是什么?

一份快照元数据,记录在它创建的那一刻仍然活跃的事务集合及相关边界。

有了它,普通 SELECT(不加锁)就能在不加行锁的前提下读到一致的数据版本。

Read View 主要字段(逻辑上)

  • m_ids:创建瞬间 仍在活跃 的事务 ID 列表(未提交)。
  • creator_trx_id:创建该 Read View 的事务 ID。
  • low_limit_idm_ids 中的最小值;如果 m_ids 为空则等于 up_limit_id
  • up_limit_id:创建瞬间下一个将要分配的事务 ID(相当于高水位)。

实际字段名在源码里是 m_ids/m_low_limit_id/m_up_limit_id/m_creator_trx_id,不同版本命名略有差异,但语义一致。

哪些隔离级别会用到 Read View?

READ COMMITTED(RC)

  • 每条一致性读都会新建一个 Read View(所以同一事务内两次查到的结果可能不同)。

REPEATABLE READ(RR)

  • 第一次一致性读创建 Read View,整个事务复用这份快照(后续一致性读结果可重复)。

READ UNCOMMITTED

  • 不创建 Read View(能读到未提交版本)。

SERIALIZABLE

  • 普通 SELECT 会被提升为加锁读(走锁,不用 Read View)。

无论什么隔离级别。

当前读SELECT … FOR UPDATE/LOCK IN SHARE MODEUPDATE/DELETE)都不用 Read View,而是读取最新版本并加锁。

可见性判定规则

给定某行的一个版本由事务 trx_id 写入,针对某个 Read View 的一致性读是否能看到它?

伪代码如下(与 InnoDB 逻辑等价):

1
2
3
4
5
6
7
8
9
10
if (trx_id == creator_trx_id)
可见(自己改的自己能看见)
else if (trx_id < low_limit_id)
可见(该版本的事务在快照创建前已提交)
else if (trx_id >= up_limit_id)
不可见(该事务在快照之后才开始)
else if (trx_id in m_ids)
不可见(快照时它还活跃,未提交)
else
可见(介于 low 与 up 之间,但快照时它已不活跃 ⇒ 已提交)

如果不可见,InnoDB 会沿着该行的 版本链(Undo 日志)回退到更早版本,重复上述判定,直到找到可见版本或无可见版本。

一致性读 VS 当前读

一致性读(Consistent Read)

普通 SELECT,通过 Read View 判定,不加锁,读到历史可见版本。

当前读(Current Read)

SELECT … FOR UPDATE / LOCK IN SHARE MODEUPDATE/DELETE

无视 Read View,直接读最新版本并加锁(Next-Key/记录锁),用于更新或防幻读。

RR 下:一致性读天然可重复;需要防幻读时,用当前读触发行间/间隙锁(Next-Key)来保证语义。

RC 与 RR 的差异直观例子

1
2
3
4
5
6
T1 (RR)                         T2
BEGIN;
SELECT * FROM t WHERE id=1; -- T1 创建 Read View,看到 v0
BEGIN; UPDATE t SET c=100 WHERE id=1; COMMIT; -- v1
SELECT * FROM t WHERE id=1; -- 仍看到 v0(因为复用同一 Read View)
COMMIT;

同样场景在 RC:第二次 SELECT 会创建 Read View,于是能看到 v1

长事务、Read View 与清理(Purge)

行的旧版本保存在 Undo 中,只有当所有比它更老的 Read View 都销毁后,Purge 线程才能真正清理这些历史版本。

长事务会拖住历史版本,导致:Undo/历史链变长、history list length 上升、二级索引回查变慢,甚至占用大量磁盘。

观测/诊断:

  • SHOW ENGINE INNODB STATUS\G 看 History List Length
  • information_schema.innodb_trx 看长事务
  • 合理控制事务时长、拆批、避免交互式长会话不提交。

常见面试问法与要点答案

Q1:Read View 何时创建?

  • RC:每次一致性读的语句开始时。
  • RR:事务第一次一致性读时,或 START TRANSACTION WITH CONSISTENT SNAPSHOT; 执行当下立即创建。

Q2:为什么 RR 能可重复读?

  • 因为整事务复用同一 Read View,后续一致性读都用同一快照判定可见性。

Q3:RR 是否靠锁避免幻读?

  • 当前读,靠 Next-Key 锁
  • 一致性读,读的是快照,不会变,但要修改满足条件的行集仍需当前读加锁来防止并发插入/删除带来的幻影。

Q4:Read View 和快照隔离是一回事吗?

  • Read View 提供的是快照可见性,InnoDB 的 RR 实际效果接近/强于标准的 Snapshot Isolation,但写写冲突由锁解决。