跳到主要内容

事务隔离级别

隔离级别定义了并发事务之间,读操作能看到怎样的数据,以及引擎为正确性付出多少锁与版本维护成本。级别越高,异常现象越少,但并发能力与实现复杂度通常更差。

MySQL InnoDB 默认:REPEATABLE READ(可重复读)。


1. 三种经典读异常​

在讲级别之前,先固定术语(以「事务 A 在读,事务 B 在写」为例):

现象含义
脏读(Dirty Read)A 读到 B 尚未提交的修改;B 若回滚,A 读到的是无效数据
不可重复读(Non-Repeatable Read)A 两次读同一行,中间 B 提交了修改,A 两次结果不同
幻读(Phantom Read)A 两次按同一条件范围查询,中间 B 提交了插入/删除,A 看到「多出行或少出行」(幻影行)

对比记忆:

  • 不可重复读侧重同一行内容变了。
  • 幻读侧重行的集合变了(多/少行)。

2. 四级隔离与能防止的现象​

SQL 标准中的四个级别:

隔离级别脏读不可重复读幻读备注
READ UNCOMMITTED可能可能可能几乎不用
READ COMMITTED否可能可能PostgreSQL 默认;Oracle 风格常见
REPEATABLE READ否否*视实现InnoDB 默认;*见下文 InnoDB 增强
SERIALIZABLE否否否最强,并发最差

「否」表示标准要求该级别不得出现该现象;具体引擎可能用锁或用 MVCC 达到类似效果。


3. InnoDB 各级别要点​

3.1 READ UNCOMMITTED​

  • 可能读到未提交数据。
  • 仅适合对正确性极不敏感的场景,生产几乎不用。

3.2 READ COMMITTED(RC)​

  • 每次普通 SELECT 看到的是当前已提交的最新版本(语句级一致性)。
  • 同一事务内两次读同一行,中间若有别人提交更新,第二次可能不同 → 允许不可重复读。
  • 行锁在语句结束等时机的行为与 RR 有差异;间隙锁使用更少,幻读/范围写入场景与 RR 不同。
  • 很多互联网业务在 RC 下也能工作,但要清楚「同一事务内两次读可能不一致」。

3.3 REPEATABLE READ(RR,InnoDB 默认)​

  • 普通快照读:在事务中第一次读时建立读视图(Read View),之后同事务内的一致读看到同一快照,从而实现可重复读。
  • InnoDB 在 RR 下通过 MVCC + 间隙锁/临键锁(在当前读时)显著缓解幻读;标准意义上的「幻读」在 InnoDB RR 中表现与教科书不完全一样,需区分快照读与当前读。

3.4 SERIALIZABLE​

  • 强制串行化调度观感:InnoDB 会把普通 SELECT 也变成类似加锁读,并发大幅下降。
  • 仅在正确性极端优先、负载可控时使用。

查看与设置(会话级示例):

SELECT @@transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

4. 快照读 vs 当前读(理解 InnoDB 的钥匙)​

类型典型 SQL行为
快照读普通 SELECT ...读历史版本(MVCC),不加行锁(一般情况)
当前读SELECT ... FOR UPDATE / LOCK IN SHARE MODE;UPDATE/DELETE 定位行读最新已提交版本并加锁

因此:

  • 「RR 下为什么我还看到幻影?」——先看你是不是 当前读,以及别的事务是否用特殊方式插入。
  • 「为什么 SELECT 没锁住别人的更新?」——普通 SELECT 往往是快照读,本来就不靠锁挡写。

更细的加锁规则见 锁机制。


5. MVCC 直观模型​

InnoDB 行上维护版本信息(简化):

行的当前版本 ← undo 链上的旧版本 ← 更旧版本 ...

每个事务有自己的 Read View,决定「哪些版本对自己可见」:

  • 自己写的未提交:自己可见。
  • 其他事务未提交:不可见(RC/RR 下)。
  • 其他事务在「视图建立之后」才提交的修改:对 RR 的快照读通常不可见。

写仍然要落到最新行并加锁,不能只靠 MVCC 解决写写冲突。


6. 如何选择隔离级别​

场景倾向建议
默认业务、希望同事务多次读取稳定保持 InnoDB 默认 RR
希望减少间隙锁、提高并发,能接受语句间读到新提交评估 RC(需回归测试)
报表长事务、只要某时刻一致快照RR 快照读或显式控制事务边界
金融核心强一致、可接受吞吐下降业务层约束 + 必要时更高隔离/串行化关键路径

不要迷信「级别越高越好」:很多问题应通过更短的事务、正确的索引与锁范围、乐观锁版本号解决,而不是一上来 SERIALIZABLE。


7. 实验建议(本地测试库)​

开两个会话,分别设置隔离级别,交叉执行:

  1. B 更新未提交,A 能否读到(脏读)。
  2. A 读一行 → B 提交更新 → A 再读(不可重复读)。
  3. A 按条件计数 → B 插入符合条件的行并提交 → A 再计数(幻读)。
  4. 对比普通 SELECT 与 SELECT ... FOR UPDATE 的差异。

下一篇:锁机制。