数据库事务隔离级别:别只背四个名词,要会在并发里做选择

6 阅读8分钟

你可能遇到过这种场景:下单高峰期,库存看起来够,真正扣减时却超卖;财务报表刚刷新,数字又变了。问题往往不在 SQL 语法,而在“并发下你允许看到什么数据”。这正是事务隔离级别要解决的事。

如果把事务看成多人同时在同一本账本上记账,隔离级别就是“你给账本上几把锁、按什么规则排队”。锁太松,容易读错;锁太紧,吞吐量会掉。工程上不是背定义,而是做取舍。

先给实战结论:大多数业务从 READ COMMITTED 起步

很多团队第一次配置数据库时,会在“默认值”和“最严格”之间摇摆。你可以先用下面这棵决策树落地。

开始
  -> 你的事务是否包含“先读再写,并且依赖读到的值做业务判断”?
      -> 是
          -> 是否属于资金总账、强一致库存这类不能容忍并发错账的场景?
              -> 是:优先 SERIALIZABLE(并准备重试机制)
              -> 否:优先 REPEATABLE READ(再做冲突测试)
      -> 否
          -> 是否以普通查询、列表页、报表读取为主?
              -> 是:优先 READ COMMITTED(SQL Server 可评估 RCSI)
              -> 否:在 READ COMMITTED 与 REPEATABLE READ 之间做压测对比

先按这棵树选一个“基线隔离级别”,再用你的真实并发流量压测,而不是直接拍脑袋上最严配置。

为什么会有四个隔离级别?先把三个并发现象看懂

先把主角说清楚再往下走。

术语解释:事务隔离级别,是数据库为并发事务定义的一组“可见性和冲突处理规则”,决定一个事务能看到哪些数据变化。

生活类比:同一栋办公室里,不同会议室的门禁权限不同;你能不能看到别人正在改的白板内容,取决于门禁级别。

迷你案例:电商系统里,事务 A 在校验库存后准备扣减,事务 B 同时修改库存。隔离级别不同,A 可能看到旧库存、也可能看到新库存,业务结果会直接不同。

1) 脏读(Dirty Read)

术语解释:读到了“别人还没提交”的数据,像看到一张还没签字生效的合同。

生活类比:同事在共享文档里改了预算,但还没点击保存,你先拿这个数字去采购。

迷你案例:事务 B 把余额从 100 改成 50 但尚未提交,事务 A 读到 50;随后 B 回滚,真实余额仍是 100,A 就基于“幻觉”做了决策。

2) 不可重复读(Non-repeatable Read)

术语解释:同一个事务里,两次读取同一行,结果不一样。

生活类比:你在会议前后各看一次排班表,都是同一格位置,名字却变了。

迷你案例:事务 A 第一次查订单状态是“待支付”;事务 B 提交更新后,A 在同一事务内再查变成“已取消”。

3) 幻读(Phantom Read)

术语解释:同一个事务里,两次按同一条件查询,第二次突然多出或少了几行。

生活类比:你点名时按“今天到岗”过滤,点完一遍再点一遍,名单里平白多了两个人。

迷你案例:事务 A 查询“库存 < 10 的商品”得到 3 行;事务 B 插入一条库存 5 的新商品并提交;A 再查同条件,变成 4 行。

四个隔离级别,像四档门禁

把隔离级别理解成“门禁强度”更直观:门越紧,错误越少,但排队可能更长。

隔离级别脏读不可重复读幻读并发性能(通常)适合场景主要代价
READ UNCOMMITTED可能发生可能发生可能发生最高极少数对准确性不敏感的临时读取数据可信度最低
READ COMMITTED可避免可能发生可能发生大多数 OLTP 业务默认起点同事务内多次读取可能不一致
REPEATABLE READ可避免可避免标准定义下仍可能发生中高先读后写、需要稳定读取窗口的交易流程锁/版本开销增加
SERIALIZABLE可避免可避免可避免最低强一致核心账务、严格规则校验冲突上升,常需重试

先根据业务容错度选一档,再测吞吐和冲突率,不要把“最严”当成“最省心”。

再看每一档的“人话版”理解:

  • READ UNCOMMITTED:几乎不设门禁。别人草稿你也能看见,速度快但容易误判。
  • READ COMMITTED:只看“已签字版本”。这是很多系统的实用平衡点。
  • REPEATABLE READ:你开会期间看到的那页表格尽量保持稳定,避免来回变脸。
  • SERIALIZABLE:把并发事务尽量排成串行执行,正确性最强,但拥堵也最明显。

主流数据库差异(截至 2026-03-04 的官方文档口径)

同名隔离级别在不同数据库里,实际行为可能不同。这部分最容易“背对了名词,做错了配置”。

数据库默认隔离级别关键实现差异实战提示
PostgreSQLREAD COMMITTEDREAD UNCOMMITTED 按 READ COMMITTED 处理;REPEATABLE READ 比标准更强,可避免幻读从 READ COMMITTED 起步,涉及跨语句一致视图可升到 REPEATABLE READ
MySQL InnoDBREPEATABLE READ支持四个标准级别,默认比很多产品更“紧”关注锁竞争与死锁重试,别只看理论级别名
SQL ServerREAD COMMITTED可通过 READ_COMMITTED_SNAPSHOT 使用行版本;在部分云形态默认开启检查数据库选项后再判断真实读一致行为
Oracle DatabaseREAD COMMITTED支持 READ COMMITTED、SERIALIZABLE、READ ONLY;不允许脏读若需要事务级稳定视图,优先评估 SERIALIZABLE 或 READ ONLY

先确认你所在数据库的“同名级别真实语义”,再写跨库通用代码和并发测试。

一眼看懂不可重复读:两会话时间线

T1(会话A): BEGIN; 设置 READ COMMITTED
T2(会话A): SELECT balance FROM account WHERE id=1; 结果=100
T3(会话B): UPDATE account SET balance=80 WHERE id=1; COMMIT
T4(会话A): 再次 SELECT balance FROM account WHERE id=1; 结果=80

这条时间线说明在 READ COMMITTED 下,同一事务内“再次读取”可能看到别人刚提交的新值,所以需要按业务判断是否升级隔离级别。

可复现实验:用 PostgreSQL 亲手跑一遍

下面这组步骤可直接在两个会话窗口复现。你会直观看到 READ COMMITTED 与 REPEATABLE READ 的差异。

-- 初始化(任一会话执行一次)
DROP TABLE IF EXISTS account;
CREATE TABLE account (
  id INT PRIMARY KEY,
  balance INT NOT NULL
);
INSERT INTO account (id, balance) VALUES (1, 100);
-- 会话A:READ COMMITTED
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT balance FROM account WHERE id = 1; -- 100

-- 保持事务不提交,切到会话B
-- 会话B
BEGIN;
UPDATE account SET balance = 80 WHERE id = 1;
COMMIT;
-- 回到会话A
SELECT balance FROM account WHERE id = 1; -- 80(发生不可重复读)
COMMIT;
-- 再做一轮:会话A改为 REPEATABLE READ
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM account WHERE id = 1; -- 假设此刻读到 80

-- 会话B再次修改并提交
-- UPDATE account SET balance = 60 WHERE id = 1; COMMIT;

-- 会话A再查
SELECT balance FROM account WHERE id = 1; -- 仍看到 80(同事务内视图稳定)
COMMIT;

把这组实验接入你的业务 SQL,再观察延迟、锁等待、重试次数,才能得到“你系统真正该用哪一级”的答案。

常见误区:不是越严越高级

误区一:一上来就全局 SERIALIZABLE。
现实:正确性确实强,但冲突和重试成本会显著上升,像把小区门禁直接改成金库门,快递员先堵在门口。

误区二:默认值一定最适合。
现实:默认值是通用平衡,不等于你的最佳点。高并发库存、跨行约束、报表一致性诉求都可能要求你改档。

误区三:只看“隔离级别名词”不看“数据库实现”。
现实:同名在不同引擎下语义可能不同,跨库迁移时最容易踩坑。

收尾:5 个可以马上执行的动作

  • 检查:先盘点你的关键事务是否存在“先读后写”的业务判断。
  • 选择:给每类事务设一个基线隔离级别,不要全站一刀切。
  • 测量:在线上相似流量下记录锁等待、死锁、重试率和 P95 延迟。
  • 测试:用双会话脚本复现脏读/不可重复读/幻读相关风险场景。
  • 验证:每次数据库升级或迁移后,重新验证默认隔离行为与并发结果。

当你把隔离级别当成“业务正确性和系统吞吐的调节旋钮”,而不是考试名词,你的并发问题会少一半。