在数据库系统的并发控制领域,隔离级别一直是工程师们在正确性与性能之间权衡的核心命题。Snapshot Isolation(快照隔离,简称 SI)因其实现相对简单且能避免大部分并发异常而被广泛采用,然而它并非真正的可序列化级别。2008 年,Cahill 等人在 SIGMOD 会议上发表的论文《Making Snapshot Isolation Serializable》首次系统性地解决了这一问题,提出了 Serializable Snapshot Isolation(可序列化快照隔离,简称 SSI)—— 一种在保持 SI 性能优势的同时确保严格可序列化性的工程化方案。
为什么 Snapshot Isolation 不是可序列化的
理解 SSI 的价值,首先需要认识到 SI 存在的正确性缺陷。SI 允许事务在其开始时创建一个数据快照,整个事务期间都只读取该快照中的数据,写操作则生成新版本。这一设计能够有效避免脏读、不可重复读和脏写等问题,然而它无法抵御一种名为「写偏斜」(Write Skew)的并发异常。
写偏斜发生在两个并发事务相互读取对方随后将修改的数据时。考虑一个经典的账户转账场景:假设账户 A 和账户 B 初始余额均为 100 元,事务 T1 读取账户 A(得到 100 元)后准备转账 50 元到账户 B,事务 T2 读取账户 B(得到 100 元)后准备转账 50 元到账户 A。在 SI 隔离级别下,两个事务各自看到的是一致的快照,各自认为转账是安全的。然而当它们分别提交时,账户 A 和账户 B 的最终余额都将变为 150 元,总额从 200 元变成了 300 元 —— 这笔「凭空产生」的资金正是写偏斜造成的数据不一致。从序列化执行的角度看,这种结果不可能出现在任何串行执行顺序中,因此 SI 不能满足真正的可序列化要求。
SSI 的核心洞察在于:写偏斜这类异常源于一种特定的数据结构,论文作者称之为「枢轴」(Pivot)。当一个事务读取了另一个事务即将修改的数据,并且自身也会进行写操作时,就形成了潜在的危险依赖。如果系统中存在多个这样的依赖形成环状结构,就会导致非序列化的执行历史。SSI 的工程实现正是通过在提交阶段检测并打破这些潜在的环结构来保证可序列化性。
依赖追踪与图环检测的工程简化
SSI 的实现本质上是多版本序列化图(Multi-Version Serialization Graph,MVSG)的构造与维护过程。当事务 T1 读取了事务 T2 写入的某个版本时,系统在图中建立一条从 T1 指向 T2 的读写依赖边。更进一步,当 T1 尝试提交时,如果它依赖于某个尚未提交的事务 T2,系统会检查这条边是否会导致序列化图中形成环。如果检测到环的存在,说明当前的事务提交顺序无法映射到任何等价的串行执行,此时系统必须中止其中一个事务以打破环结构。
这种设计的关键工程简化在于:依赖追踪与环检测仅在事务提交时进行,而非在整个事务生命周期中持续运行。与传统的两阶段锁(2PL)相比,SSI 避免了长时间的锁持有,极大提升了并发度;与严格的乐观并发控制相比,SSI 仅需要检查读写依赖,无需记录所有字段的详细变更。PostgreSQL 在 9.1 版本中首次实现了 SSI,成为首个在生产级数据库中支持该特性的主流系统。其实现通过维护一个轻量级的依赖记录结构,在提交验证阶段高效地检测潜在的序列化冲突。
PostgreSQL SSI 的关键配置参数
对于在生产环境中使用 PostgreSQL 的工程师而言,理解 SSI 的行为特征并合理配置至关重要。PostgreSQL 将 SSI 实现为其 SERIALIZABLE 隔离级别,与传统的 REPEATABLE READ 级别相比,SERIALIZABLE 能够保证严格的序列执行等价性,但可能在高并发写入场景下产生更多事务中止。
在实际部署中,有几个关键参数值得工程师关注。首先是 max_connections—— 由于 SSI 需要为每个活跃事务维护读写依赖信息,过多的并发连接会显著增加内存压力。其次是事务执行时间:长时间运行的事务会扩大依赖图的规模,增加冲突检测的计算开销,同时增加其他事务被中止的概率。因此,工程师应当尽量将事务控制在较短的执行时间内,避免在事务内进行不必要的网络交互或复杂计算。
对于读多写少的工作负载,SSI 的性能开销通常可以忽略不计,因为大多数事务仅创建依赖边而不会触发中止。对于写入密集型应用,建议通过窄范围写入、避免热点行的频繁更新、批量操作合并等手段降低事务间的读写依赖冲突。PostgreSQL 还提供了 default_transaction_isolation 参数,开发者可以根据业务场景灵活切换隔离级别 —— 对正确性要求极高的金融交易场景使用 SERIALIZABLE,而对性能更敏感的统计分析场景使用 REPEATABLE READ。
正确性简化的工程权衡
SSI 的设计哲学体现了一种深邃的工程思维:将复杂的正确性证明转化为可执行的图算法,同时保持足够简洁以适用于生产环境。传统的可序列化实现依赖两阶段锁,虽然正确性容易证明,但性能代价高昂;SSI 则通过在提交时刻的轻量级验证实现了「正确性延迟付费」,在大多数情况下获得与 SI 相近的性能,仅在必要时才付出中止的代价。
这种简化路径对于数据库开发者和使用者都具有重要启示。对于数据库实现者而言,SSI 提供了一条相对平坦的工程化路径:在现有的 MVCC 快照隔离基础上增加依赖追踪与提交验证模块,即可获得可序列化保证。对于应用工程师而言,理解 SSI 的行为特性有助于设计更低冲突的事务模式 —— 例如将相关数据的读写集中在同一事务中、避免跨事务的读后写模式、合理使用显式锁定等。
从更宏观的视角看,SSI 代表了数据库并发控制领域的一次重要范式转变。它证明了在保证严格正确性的前提下,依然可以追求实现的简洁与性能的卓越。对于追求系统正确性的工程师而言,SSI 不仅是一项技术选型,更是一种工程哲学的实践 —— 通过深入理解问题的本质结构,找到既正确又简洁的解决方案。
参考资料
- Cahill, M. J., Röhm, U., & Fekete, A. D. (2008). Making Snapshot Isolation Serializable. Proceedings of the 2008 ACM SIGMOD International Conference on Management of Data.
- PostgreSQL Wiki: SSI. https://wiki.postgresql.org/wiki/SSI