事务隔离级别

事务隔离级别定义并发事务之间的可见性和冲突处理强度,是理解脏读、不可重复读、幻读与并发代价的核心入口。

#type / concept #status / growing #tech / dev / backend #resource / database

[!info] related notes

事务隔离级别

一句话定义

事务隔离级别定义了并发事务之间能看到多少彼此的中间结果,以及数据库愿意为此付出多少锁、版本管理和等待成本。

为什么事务还需要“隔离级别”

事务并不天然意味着“并发下一定安全”。

单个事务内部可以是原子的,但多个事务并发运行时仍然会出现:

  • 读到别人还没提交的数据
  • 同一事务里前后两次读取结果不一致
  • 条件查询前后结果集变化
  • 两个事务基于旧值同时做决定,最终互相覆盖

隔离级别就是数据库给出的并发语义选择:

  • 保证更强,通常意味着更多等待、锁冲突或重试成本
  • 保证更弱,通常意味着更高吞吐,但应用要接受更多并发异常风险

核心机制 / 工作原理

1. 隔离级别本质上在定义“谁能看见什么”

一个事务在执行过程中,最关键的问题包括:

  • 它能不能看到别的事务未提交的修改
  • 它在同一次事务里第二次读取时,是否还能看到和第一次一样的数据
  • 它按某个条件查询出的结果集,之后会不会凭空多出或少掉一些行

隔离级别就是对这些可见性问题做不同强度的承诺。

2. 常见的四级隔离

常见隔离级别从低到高通常是:

  1. 读未提交 READ UNCOMMITTED
  2. 读已提交 READ COMMITTED
  3. 可重复读 REPEATABLE READ
  4. 串行化 SERIALIZABLE

它们常围绕三类经典异常来讲:

  • 脏读:读到了别的事务还没提交的数据
  • 不可重复读:同一事务里两次读同一行,结果不一致
  • 幻读:同一事务里两次按同条件查询,结果集行数变了

但真实系统里,光背这三类还不够,因为很多业务 bug 其实更接近“丢失更新”或“写偏斜”。

3. 不同隔离级别,数据库通常会动用不同工具

数据库并不是单靠一个配置开关就实现隔离,而是会组合:

  • MVCC:决定读事务看到哪个版本
  • 数据库锁:在必要时阻止别的事务继续读写某些资源
  • 调度和冲突检测:在更强隔离下让某些事务等待、失败或重试

所以隔离级别真正决定的是:

  • 当前事务看到的数据快照有多稳定
  • 并发事务之间允许多大程度的交错
  • 冲突发生时数据库更倾向于“阻塞”还是“回滚其中一方”

四个级别可以怎样理解

1. 读未提交

这是最弱的隔离。

它允许事务读到别的事务尚未提交的数据,所以会有明显的脏读风险。工程上很少作为默认业务隔离使用,因为它让“看到的值到底算不算数”本身都变得不可靠。

2. 读已提交

它至少保证:

  • 你不会读到别人尚未提交的数据

但它不保证:

  • 同一事务里两次读取同一行还能看到同样结果
  • 条件查询前后结果集不变

很多数据库把它作为默认隔离,因为它在正确性和吞吐之间比较均衡。

3. 可重复读

它想解决的是:

  • 同一事务中反复读同一批数据时,希望看到一个更稳定的视图

这通常意味着:

  • 事务会基于某个一致性快照工作
  • 对同一行的重复读取更容易保持一致

但不同数据库对“可重复读”这个名字背后的实现并不完全相同,所以不能只背标准名字,不看产品细节。

4. 串行化

这是最强的隔离目标。

它希望并发事务的最终效果等价于“一个一个顺序执行”。这能减少大量并发异常,但代价通常是:

  • 更多锁冲突或序列化失败
  • 更多等待
  • 更高重试成本

所以它通常不是默认开到最强,而是只在某些强一致业务边界里使用。

最小例子 / 最小场景

假设事务 A 查询“余额大于 1000 的账户数”,事务 B 在中间插入一条符合条件的新账户:

  • 隔离较弱时,事务 A 第二次再查,结果集可能多一行
  • 隔离更强时,事务 A 可能继续看到第一次查询时的同一个一致性视图
  • 再更强时,事务 B 甚至可能被阻塞,或者其中一方需要回滚重试

这个例子说明,隔离级别关注的不是某一条 SQL 的语法,而是并发执行时系统如何安排可见性和冲突。

从理解顺序上,最该先抓住什么

1. 隔离级别不是“越高越好”

更高隔离通常意味着:

  • 更长等待时间
  • 更多锁
  • 更高资源消耗
  • 更高事务失败和重试概率

所以选择隔离级别,本质上是在“正确性风险”和“并发吞吐”之间做工程权衡。

2. 名字相同,不代表实现相同

同样叫 REPEATABLE READ,MySQL 和 PostgreSQL 在:

  • 版本可见性实现
  • 是否使用 gap lock 一类额外锁策略
  • 幻读和范围更新的处理方式

上都可能不同。

3. 应用真正关心的是“哪些异常我不能接受”

例如:

  • 转账、扣库存、抢名额,往往比普通列表查询更怕并发写冲突
  • 报表或后台浏览场景,通常可以接受更弱隔离换更高吞吐

因此先识别业务不变量,再倒推隔离级别,通常比反过来更合理。

边界与易混淆点

  • 隔离级别主要描述并发可见性和调度强度,不直接解决“提交后宕机还能不能恢复”,那是持久性和日志系统的问题。
  • 事务隔离级别不是单独工作的,它要和 MVCC数据库锁 一起理解。
  • “不会脏读”不等于“所有并发异常都消失了”。
  • 幻读不是“看到了假数据”,而是条件查询对应的结果集在同一事务里发生了变化。
  • 如果应用把本该在一个事务中的检查和更新拆开,再高的隔离级别也未必能自动替你纠正业务设计问题。
创建于 2026/4/9 更新于 2026/5/27