“消息传递中的崩溃使 2PC 依赖 write ahead log、幂等消息、超时与人工或启发式恢复。它协调数据库分片、消息系统和其他跨资源事务的 commit/abort,但不提供事务隔离;…”
形式陈述 ​
快照隔离(snapshot isolation, SI)为每个事务
更新事务提交时执行 first-committer-wins 检查。若存在已提交事务
则两者不能都提交;先通过提交点者获胜,另一方中止。更常见的实现表述是:
SI 是一种事务历史语义,MVCC是实现它的常见机制。版本链、事务 ID 与垃圾回收并不自动构成 SI;系统还需保证快照在所有读取上对应同一逻辑时点,并在提交处原子执行写写冲突检查。相同 MVCC 基础也可实现其他隔离级别。
从依赖图看,SI 排除了含有并发写写冲突的已提交组合,但允许读写反依赖:事务
直觉 ​
SI 像为事务在开始时拍下一整套数据库底片。事务之后无论运行多久,都从这套底片读取,并把自己的修改叠在私有层上。提交时系统只阻止两个并发事务改动同一数据项;只读者通常无需挡住写者,更新者也不会改变已开始事务的视图。
这种设计消除了脏读和事务内视图跳变,也避免许多同项覆盖,却看不见跨多行约束。两个事务可能依据同一旧快照分别修改不同记录,各自都没有写写冲突,合起来却让业务不变量失效。
例子与边界 ​
表 on_call(doctor, active) 初始有两行:医生 Alice 与 Bob 的 active 都为真,业务约束是至少一名医生值班。两个事务并发开始,并取得同一快照:
T_A: read Alice=true, Bob=true
because Bob is active, write Alice=false
T_B: read Alice=true, Bob=true
because Alice is active, write Bob=false
{Alice},{Bob},两者不相交,因此 first-committer-wins 不会中止任何一方。两笔事务都提交后,两行均为假,违反“至少一人值班”。历史中,
这不是 lost update。两笔事务写的是不同记录,没有任何一个写覆盖另一个写;异常来自分别读取对方将要改动的旧版本,再把两个决策组合起来。若强行把例子改成双方都写同一行,SI 会让后提交者中止,反而失去 write skew 的核心结构。
作为对照,两个事务并发修改同一医生记录,或争用同一个唯一座位。它们的写集相交,先提交者发布新版本,另一方在提交检查中发现冲突并中止。这说明 SI 确实解决一类冲突,只是保护单位停留在写项,没有自动覆盖跨行谓词。
推论与应用 ​
快照隔离适合读多写少、长查询需要稳定视图的工作负载。它常能避免读写阻塞并提供比 read committed 更易理解的事务内观察,但系统文档必须明确快照形成时刻、并发定义、写冲突粒度以及唯一约束和谓词更新怎样处理;产品名称中的“snapshot”不保证完全采用经典 SI。
若应用需要可串行化,可在 SI 之上引入谓词锁、显式锁定关键汇总行,或使用 Serializable Snapshot Isolation 检测危险的读写反依赖结构并中止部分事务。SSI 是更强的并发控制策略,不应倒写进 SI 定义。SI 同样不自动提供外部一致性、持久性或分布式原子提交,这些性质需要独立协议。
参考资料
- Hal Berenson et al., “A Critique of ANSI SQL Isolation Levels,” SIGMOD 1995, pp. 1–10。
- Alan Fekete et al., “Making Snapshot Isolation Serializable,” ACM Transactions on Database Systems 30(2), 2005, pp. 492–528。
- Michael J. Cahill, Uwe Röhm, and Alan D. Fekete, “Serializable Isolation for Snapshot Databases,” SIGMOD 2008, pp. 729–738。