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

目标:你能把隔离级别从"背定义"变成"能推导现象",并解释清楚:

  • RR/RC 的一致性读到底读的是什么
  • 为什么还会有幻读、什么时候靠锁解决
  • MVCC 与当前读的分工

1. 先背现象:四个隔离级别对应什么问题

  • RU:可能脏读
  • RC:不脏读,但可能不可重复读
  • RR:解决不可重复读,但"幻读"需要结合锁语义理解
  • Serializable:最强,通常并发能力最差

面试关键不是背表格,而是能举例。

2. 两类读:一致性读(快照) vs 当前读(加锁)

2.1 一致性读(Consistent Read)

  • 读的是"某个时间点的快照"
  • 不加锁(或加极轻量的元数据/意向锁)
  • 依赖 MVCC

2.2 当前读(Current Read)

  • 读的是"最新已提交/可见的版本",并且通常会加锁
  • 常见语句:
    • select ... for update
    • select ... lock in share mode
    • update/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-key)

5. 结合例子推导(建议面试这样讲)

表:t(id primary key, k int, index(k))

最小建表:

sql 复制代码
create table t (
  id int primary key,
  k int not null,
  v int not null,
  key idx_k(k)
);
insert into t values (1, 10, 10);

5.1 RC 的不可重复读

  • T1:select * from t where id=1; 读到 v=10
  • T2:update t set v=20 where id=1; commit;
  • T1:再查 id=1,可能读到 v=20

对照点:RC 允许"同一事务内两次快照读看到不同提交结果",因为每次读可能生成新的 Read View。

5.2 RR 的可重复读(快照读)

  • T1:第一次 select 生成 Read View
  • T2 更新并提交
  • T1 再 select 仍读到旧版本(沿版本链找可见版本)

对照点:RR 的快照读更像"拍了一张照片",后面继续看这张照片,直到事务结束。

5.3 当前读下的"范围问题"

  • T1:select * from t where k between 10 and 20 for update;

这会在 RR 下对范围加 next-key 锁,阻止 T2 插入落在范围内的新行。

一个更具体的并发时序:

  • T1:begin; 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(从现象到机制)

  1. 先确认"你用的是什么读"
    • 普通 select:快照读
    • for update:当前读 + 加锁
  2. 先确认隔离级别(RC/RR)
    • RC:可能不可重复读(快照随读变化)
    • RR:快照固定,但当前读仍会加锁影响并发
  3. 出现阻塞/死锁时,优先看"范围是否过大"
    • between / 非唯一条件 -> next-key 覆盖面大
  4. 出现历史版本堆积/存储膨胀时,优先怀疑长事务
    • 事务时间长会让旧版本无法回收,undo 膨胀
  5. 需要强一致业务判断时
    • 用当前读(for update)并确保 where 足够精确
相关推荐
三品PLM系统8 分钟前
【无标题】
数据库·制造·cadence·plm
ITxiaobing202323 分钟前
如何构建IP维度的假量排查流程:以AppsFlyer Protect360判假后的实战为例
网络·数据库
ywl4708120871 小时前
mysql 锁定读和非锁定读
数据库·mysql
流浪0011 小时前
MySQL数据库基础篇(三):MySQL 数据库库级操作全解:创建、字符集、管理与备份恢复实战
数据库·mysql
昌旭咨询1 小时前
2026国内知名的CS认证咨询机构有哪些?企业该如何选择?
大数据·数据库·人工智能
吴声子夜歌2 小时前
Redis 3.x——集群运维
运维·数据库·redis
钝挫力PROGRAMER2 小时前
MySQL 数据截断错误 #22001:原因分析与解决方案
数据库·mysql
Database_Cool_2 小时前
记忆张量MemOS + 阿里云PolarDB一站式记忆管理方案发布:给AI装上不断片的记忆
数据库·人工智能·阿里云
码农阿豪2 小时前
上线前能跑,上线后挂了,三个隐性的SQL陷阱
数据库·sql
春卷同学2 小时前
032-关系型数据库在记账应用中的建模与查询优化
java·jvm·数据库