事务隔离级别与一致性:从现象到实现(MVCC 与当前读)

张开发
2026/5/22 23:02:41 15 分钟阅读
事务隔离级别与一致性:从现象到实现(MVCC 与当前读)
目标你能把隔离级别从“背定义”变成“能推导现象”并解释清楚RR/RC 的一致性读到底读的是什么为什么还会有幻读、什么时候靠锁解决MVCC 与当前读的分工1. 先背现象四个隔离级别对应什么问题RU可能脏读RC不脏读但可能不可重复读RR解决不可重复读但“幻读”需要结合锁语义理解Serializable最强通常并发能力最差面试关键不是背表格而是能举例。2. 两类读一致性读快照 vs 当前读加锁2.1 一致性读Consistent Read读的是“某个时间点的快照”不加锁或加极轻量的元数据/意向锁依赖 MVCC2.2 当前读Current Read读的是“最新已提交/可见的版本”并且通常会加锁常见语句select ... for updateselect ... lock in share modeupdate/delete/insert直觉你要修改数据就必须看“现在的真实值”并锁住它防止并发写。2.3 对照组同样一条 select加不加锁语义完全不同以 InnoDB 为例快照读select ...一致性读走 MVCC当前读select ... for update/lock in share mode加锁读面试表达建议“隔离级别 读类型”一起决定你看到什么、会不会阻塞别人3. MVCC 的最小模型版本链 Read View在 InnoDB 的直觉模型里每行有隐藏字段记录版本信息更新会生成新版本旧版本通过 undo log 形成版本链一致性读会生成 Read View读视图用它决定哪些事务的版本对我可见3.1 RC vs RR 的关键区别面试常问RC每次一致性读可能生成新的 Read View所以同一事务内两次查询可能看到不同结果不可重复读RR事务第一次一致性读生成 Read View后续复用所以可重复读4. 幻读怎么理解不要只背“RR 解决幻读/没解决幻读”幻读的经典定义是同一事务内两次范围查询返回的“行集合”不同在 InnoDB 中单纯一致性读快照读在 RR 下通常不会看到新插入行因为 Read View 复用但当你做当前读for update / update时需要锁住范围才防止并发插入导致“范围内出现新行”因此“幻读”是否出现取决于你用的是快照读还是当前读要彻底防止并发插入需要范围锁next-key5. 结合例子推导建议面试这样讲表t(id primary key, k int, index(k))最小建表createtablet(idintprimarykey,kintnotnull,vintnotnull,keyidx_k(k));insertintotvalues(1,10,10);5.1 RC 的不可重复读T1select * from t where id1;读到 v10T2update t set v20 where id1; commit;T1再查 id1可能读到 v20对照点RC 允许“同一事务内两次快照读看到不同提交结果”因为每次读可能生成新的 Read View。5.2 RR 的可重复读快照读T1第一次 select 生成 Read ViewT2 更新并提交T1 再 select 仍读到旧版本沿版本链找可见版本对照点RR 的快照读更像“拍了一张照片”后面继续看这张照片直到事务结束。5.3 当前读下的“范围问题”T1select * from t where k between 10 and 20 for update;这会在 RR 下对范围加 next-key 锁阻止 T2 插入落在范围内的新行。一个更具体的并发时序T1begin; select * from t where k between 10 and 20 for update;T2尝试insert into t(id,k,v) values(2,15,1);可能会被阻塞直觉你既然要对“范围内的数据”做修改或依赖它做业务判断就必须防止并发插入改变集合6. 常见坑与排查“我用 RR 还出现数据不一致”你是不是混用了快照读与当前读是否在同一事务里先 select 后 update理解版本可见性“范围更新导致锁冲突严重”next-key 锁覆盖范围插入被阻塞用更精确条件或拆批减少锁范围“长事务”导致 undo 膨胀RR 快照长期存在旧版本无法回收需要控制事务时长6.1 更流程化的排查 checklist从现象到机制先确认“你用的是什么读”普通 select快照读for update当前读 加锁先确认隔离级别RC/RRRC可能不可重复读快照随读变化RR快照固定但当前读仍会加锁影响并发出现阻塞/死锁时优先看“范围是否过大”between/ 非唯一条件 - next-key 覆盖面大出现历史版本堆积/存储膨胀时优先怀疑长事务事务时间长会让旧版本无法回收undo 膨胀需要强一致业务判断时用当前读for update并确保 where 足够精确

更多文章