本文是《Designing Data-Intensive Applications》(DDIA,中文译名《数据密集型应用系统设计》)第 7 章的导读。DDIA 是 Martin Kleppmann 所著的分布式系统经典,本系列逐章导读,把书的核心概念讲清楚。
一句话主旨
事务是"把多个操作打包成一个原子单元"的抽象——要么全做,要么全不做。但"全做全不做"在并发和故障下要付出代价,而代价的大小取决于你选多强的隔离级别。本章的核心不是 ACID 的定义,而是隔离级别的谱系:从最弱的"读未提交"到最强的"可串行化",每一级挡掉一类并发异常,也付出一份性能代价。
核心概念拆解
1. ACID——四个保证,但 A 和 I 是重点
- A 原子性:一组操作要么全成功要么全回滚。靠 undo log / WAL 实现。
- C 一致性:数据的业务不变量(如"余额不为负")在事务后仍成立。C 是应用层责任,数据库提供 A/I/D 帮你达到 C,但 C 本身要应用定义。
- I 隔离性:并发事务互不干扰,效果如同串行执行。这是本章主角——完整隔离(可串行化)代价高,所以有各级别。
- D 持久性:事务提交后即使断电也不丢。靠 WAL + fsync。
A、I、D 是数据库机制,C 主要是应用责任。第 7 章几乎全在讲 I(隔离级别),因为 I 是性能与正确性权衡的主战场。
2. 隔离级别的谱系——每级挡一类异常
这是本章的重头。并发事务会互相干扰,产生各种"异常"(anomaly)。隔离级别从弱到强,每级挡掉一类异常,代价是性能下降。
四种并发异常(从轻到重):
① 脏读(Dirty Read)——读到别人还没提交的数据。对方回滚了,你读的就成了"从未存在过的数据"。读未提交级别下发生,读已提交挡掉。
② 不可重复读(Non-repeatable Read)——同一事务里读同一行两次,值变了(别人提交了更新)。读已提交下发生,可重复读挡掉。
③ 幻读(Phantom Read)——同一事务里范围查询两次,结果集行数变了(别人提交了插入/删除)。可重复读挡行级读一致但挡不住范围新增,快照隔离挡掉。
④ 写偏斜(Write Skew)——两个事务各自读了一致快照,基于快照各自做决策并写入,两个写入合在一起违反了业务约束,但单独看都不违规。快照隔离挡不住这个,只有可串行化挡得住。
写偏斜是快照隔离的盲区,也是最难直觉理解的异常。经典例子:值班规则要求"至少一人值班",A 和 B 都在值班,两人同时申请休假——各自快照里看到"对方在值班,我走没关系",都提交休假,结果没人值班。单独看每个事务都合规,合起来违反约束。只有可串行化能防。
问题→方案:问题——并发操作互相干扰产生异常。场景——多个事务同时读写相同数据,弱隔离下出现脏读/不可重复读/幻读/写偏斜。方案——隔离级别谱系:每升一级挡掉一类异常,代价是性能下降。读已提交挡脏读,可重复读挡不可重复读,快照隔离靠 MVCC 让读写不互锁(读历史版本、写写冲突先到先赢),可串行化靠 2PL 或 SSI 挡写偏斜。选哪级 = 你能容忍哪种异常 vs 愿付多少性能代价。
3. 快照隔离(SI)——现代主流
核心机制——MVCC(多版本并发控制):
- 每行数据有多个版本(带时间戳/事务号)
- 事务开始时"拍一个快照"——只看开始时刻已提交的版本
- 读不阻塞写,写不阻塞读(各看各版本)——读写不互锁,这是 SI 高性能的关键
- 写写冲突检测:两个事务同时改同一行,后提交的被检测到冲突并回滚(first-committer-wins)
大多数现代 OLAP/HTAP 系统都用 MVCC + 快照隔离。分析负载以读为主,弱隔离够用且快得多。
4. 乐观锁 vs 悲观锁——两种并发控制思路
悲观锁:先锁再改。冲突多时高效(少重试),但锁等待开销大、死锁风险。
乐观锁(OCC):先干再提交时验证。冲突少时高效(无锁开销),冲突多时大量重试反而慢。
5. 两阶段锁(2PL)——可串行化的经典实现
直觉:事务分两阶段——扩张阶段(只加锁不释放)和收缩阶段(只释放不加锁)。只要严格遵守 2PL,结果必然可串行化。
2PL 的代价:
- 锁等待多,并发度低
- 死锁:A 等 B 的锁,B 等 A 的锁 → 要检测并杀一个
- 性能远不如 MVCC
用纯 2PL 的在减少,因为锁开销大、死锁频繁。大多数用 MVCC + 快照隔离,只在需要可串行化时才上 2PL 或 SSI(Serializable Snapshot Isolation,一种结合快照和冲突检测的可串行化方案)。
6. 可串行化的现代实现——SSI
SSI(Serializable Snapshot Isolation):在快照隔离基础上,额外检测"写偏斜"模式的冲突——跟踪事务的读依赖,若多个事务的读依赖可能形成写偏斜,abort 其中一个。比 2PL 性能好(仍是 MVCC 无锁读),但比 SI 有额外跟踪开销。
DDIA 重点是让你知道:可串行化有三条路线:实际串行执行(VoltDB/Redis)、2PL、SSI,其中 SSI 是现代更好的选择,但大多数系统默认不开(快照隔离够用且更快)。
Mermaid:隔离级别与异常对照
下一篇:第 8 章——分布式系统的麻烦。分布式系统的部分失效、不可靠网络与时钟。