可重复读(rr)下,readview在事务首次快照读(普通select)时生成并全程复用;begin不触发,当前读也不触发;其四个字段共同决定行版本可见性。

可重复读(RR)隔离级别下,MVCC 的核心行为是:事务第一次执行 SELECT 时生成一个全局复用的 ReadView,后续所有快照读都基于它判断行版本可见性——不是“锁住数据”,而是“锁定快照视角”。
ReadView 什么时候生成?只生成一次
在 RR 级别下,ReadView 不是每次 SELECT 都新建,而是在事务中**第一条非锁定读语句(即普通 SELECT)执行时生成**,之后整个事务生命周期内复用它。
- 如果事务先执行
UPDATE或SELECT ... FOR UPDATE(当前读),再执行普通SELECT,则ReadView仍是在第一个普通SELECT时才创建 - 显式开启事务(
BEGIN或START TRANSACTION)本身不触发ReadView生成 - 这意味着:即使事务持续数分钟,只要没执行过快照读,就还没形成一致性视图
ReadView 四个关键字段怎么决定某行是否可见?
判断某行记录是否对当前事务可见,本质是比对该行的 DB_TRX_ID(最后修改它的事务 ID)与 ReadView 中的四个字段:m_ids、min_trx_id、max_trx_id、creator_trx_id。
- 若
DB_TRX_IDmin_trx_id → 该事务在快照生成前已提交,可见 - 若
DB_TRX_ID≥max_trx_id→ 该事务在快照生成后才启动,不可见 - 若
DB_TRX_ID∈m_ids→ 该事务在快照生成时仍活跃(未提交),不可见 - 若
DB_TRX_ID==creator_trx_id→ 当前行由本事务自己修改,可见(即使未提交)
只有满足任一「可见条件」,才会顺着 DB_ROLL_PTR 在 undo log 版本链中继续向上找更老版本,直到找到一个可见版本或链尾。
为什么 RR 能避免不可重复读,却无法解决幻读?
不可重复读靠的是 ReadView 复用:同一事务内多次 SELECT 查同一行,因 ReadView 不变,DB_TRX_ID 判断结果一致,总看到相同版本。
- 但幻读是新插入的行(
INSERT)带来的——这些新行的DB_TRX_ID很可能 ≥max_trx_id,直接被判定为「不可见」;可一旦其他事务提交了插入,且该新行的DB_TRX_ID落在[min_trx_id, max_trx_id)区间且不在m_ids中,它就突然“冒出来”了 - InnoDB 对 RR 级别的幻读仅靠
Next-Key Lock(间隙锁+记录锁)缓解,不是 MVCC 本职工作 - 所以 MVCC 保证的是「已存在行的版本可见性稳定」,不承诺「查询范围内的行集合稳定性」
RC 和 RR 的 ReadView 差异在哪?一句话区分
RC 每次 SELECT 都新建 ReadView,因此能立刻看到其他事务刚提交的更新;RR 只建一次,所有后续 SELECT 共享初始快照。
- 这导致 RC 下会出现不可重复读(同一条记录两次查出不同值),而 RR 下不会
- 但两者都依赖 undo log 版本链和
DB_TRX_ID/DB_ROLL_PTR这套底层结构,MVCC 机制本身是共通的 - 真正差异不在“有没有 MVCC”,而在“
ReadView生命周期策略”
容易忽略的一点:ReadView 的 max_trx_id 是“下一个将分配的事务 ID”,不是“当前最大已提交 ID”,所以它本身不反映提交状态,只是个边界标记。











