ReadView是干嘛的?
ReadView是干嘛的?
月伴飞鱼它是 InnoDB 实现 MVCC 的核心,用来判断某个行版本,对当前这次一致性读取是否可见。
Read View 是什么?
一份快照元数据,记录在它创建的那一刻仍然活跃的事务集合及相关边界。
有了它,普通
SELECT
(不加锁)就能在不加行锁的前提下读到一致的数据版本。
Read View 主要字段(逻辑上)
m_ids
:创建瞬间 仍在活跃 的事务 ID 列表(未提交)。creator_trx_id
:创建该 Read View 的事务 ID。low_limit_id
:m_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 MODE
、UPDATE/DELETE
)都不用 Read View,而是读取最新版本并加锁。
可见性判定规则
给定某行的一个版本由事务
trx_id
写入,针对某个 Read View 的一致性读是否能看到它?
伪代码如下(与 InnoDB 逻辑等价):
1 | if (trx_id == creator_trx_id) |
如果不可见,InnoDB 会沿着该行的 版本链(Undo 日志)回退到更早版本,重复上述判定,直到找到可见版本或无可见版本。
一致性读 VS 当前读
一致性读(Consistent Read):
普通
SELECT
,通过 Read View 判定,不加锁,读到历史可见版本。当前读(Current Read):
SELECT … FOR UPDATE
/LOCK IN SHARE MODE
、UPDATE/DELETE
。无视 Read View,直接读最新版本并加锁(Next-Key/记录锁),用于更新或防幻读。
在 RR 下:一致性读天然可重复;需要防幻读时,用当前读触发行间/间隙锁(Next-Key)来保证语义。
RC 与 RR 的差异直观例子
1 | T1 (RR) T2 |
同样场景在 RC:第二次
SELECT
会创建新 Read View,于是能看到v1
。
长事务、Read View 与清理(Purge)
行的旧版本保存在 Undo 中,只有当所有比它更老的 Read View 都销毁后,Purge 线程才能真正清理这些历史版本。
长事务会拖住历史版本,导致:Undo/历史链变长、
history list length
上升、二级索引回查变慢,甚至占用大量磁盘。观测/诊断:
SHOW ENGINE INNODB STATUS\G
看 History List Lengthinformation_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,但写写冲突由锁解决。