1 基本概念

可串行化是数据库并发控制中的核心正确性标准之一。它关注的是:当多个事务同时执行时,系统最终呈现出的结果,是否与这些事务某一种串行执行顺序下的结果完全一致。若满足这一条件,则可认为并发调度在逻辑上是“安全”的。

1.1 定义

可串行化通常指一组并发事务的执行调度,能够等价于某个串行调度。这里的“等价”一般强调事务对数据库状态的读写效果一致,即无论事务实际如何交错,只要最终结果和某个按顺序逐个执行的方案相同,就可视为可串行化。

1.2 核心目标

其主要目标是保证并发执行不会破坏数据一致性数据库系统希望在提高并发度的同时,仍然避免脏数据、丢失更新、不可重复读等典型异常,从而让应用层看到一个可预测的事务执行结果。

1.3 与串行执行的关系

串行执行是最直观的事务执行方式,即事务一个接一个完成,后一个事务必须等待前一个事务结束。可串行化并不要求事务真的按顺序运行,而是要求并发运行的效果“看起来像”某种串行顺序。也就是说,可串行化是一种结果上的等价,而不是过程上的限制。

1.4 与并发执行的关系

并发执行允许多个事务交错访问数据,以提升系统吞吐量资源利用率。但交错执行也会带来一致性风险,因此并发控制机制需要在性能与正确性之间取得平衡。可串行化正是衡量这种平衡是否达标的重要标准。

2 理论基础

2.1 事务与调度

事务是数据库中一组作为整体处理的操作集合,通常包含读、写、提交与回滚等动作。调度则描述多个事务操作在时间上的交错顺序。数据库并发控制研究的重点,就是在各种调度中识别哪些是可接受的,哪些会导致异常结果。

2.2 可串行化调度

如果一个并发调度与某个串行调度等价,则称其为可串行化调度。它是事务并发执行中最重要的理想目标之一。很多并发控制协议的设计,实际上都是为了尽量确保生成的调度满足这一性质。

2.3 冲突可串行化

冲突可串行化是可串行化的一种重要判定方式,也是实践中最常见的分析模型。它基于操作之间是否存在冲突来判断调度是否可等价重排为串行顺序。

2.3.1 冲突操作

若两个操作来自不同事务,访问同一数据项,且至少有一个是写操作,则这两个操作构成冲突。常见冲突包括读写冲突、写读冲突以及写写冲突。冲突操作的相对顺序通常不能随意交换,否则可能改变最终结果。

2.3.2 优先图与判定

优先图也称前驱图,用于表示事务之间的先后约束关系。图中的节点代表事务,若事务A中的某个冲突操作先于事务B中的对应操作,则在图中从A指向B。若该图存在环,则该调度不是冲突可串行化的;若无环,则可按拓扑序得到一个等价串行顺序。

2.4 视图可串行化

视图可串行化比冲突可串行化更宽松。它不仅要求结果等价,还关注每个读操作所观察到的写入来源,以及最终写入的归属关系。某些调度虽然无法通过冲突关系重排为串行顺序,但仍可能在视图层面与某个串行调度等价。

3 判定方法

3.1 基于冲突图的判定

实际系统和教材中最常用的方法,是构建优先图并检查是否存在环。该方法直观、易实现,且对许多常见调度足够有效。对于无环图,可以进一步给出一个满足约束的串行顺序。

3.2 基于视图等价的判定

视图等价判定需要比较更细粒度的读写关系,包括初始读、读到谁的写结果、以及最终写者等信息。它比冲突图判定更全面,但分析过程通常更复杂,因此更多用于理论研究或特殊场景分析。

3.3 判定复杂度

从计算复杂度看,冲突可串行化的判定相对容易,通常可以在线性或接近线性的图遍历框架下完成。视图可串行化的判定则明显更困难,在一般情况下具有更高的复杂度,因此不如冲突判定常见。

3.4 常见反例边界情况

一些调度在局部看来并无明显冲突,但由于多个事务之间形成隐含依赖,最终仍会出现不可串行化的情况。另一些调度则可能包含“盲写”等特殊操作,使得冲突可串行化与视图可串行化的结论不一致。此类边界情况常用于说明两种判定标准的差异。

4 实现机制

4.1 锁协议

锁机制是实现可串行化最经典的办法之一。通过对数据项加锁,系统限制事务对共享资源的并发访问方式,从而避免不安全的交错。

4.1.1 两阶段锁协议

两阶段锁协议要求事务先在增长阶段获取所需锁,在收缩阶段释放锁,且一旦开始释放,就不能再申请新锁。它能够保证事务调度具有冲突可串行化性质,因此被广泛应用于传统数据库系统。

4.1.2 严格两阶段锁

严格两阶段锁进一步要求事务持有写锁直到提交或回滚结束。这样不仅能保证冲突可串行化,还能简化故障恢复过程,减少脏读和级联回滚的风险,因此在工程中十分常见。

4.2 时间戳排序

时间戳排序通过为事务分配全局顺序标识,强制所有冲突操作按照时间戳顺序执行。若某事务的操作违反了既定顺序,系统通常会使其回滚并重新执行。该方法不依赖加锁即可维持一致性,但在冲突较多时可能导致较高回滚率。

4.3 多版本并发控制

多版本并发控制通过保留数据项的多个历史版本,使读操作可以读取较早但一致的快照,而写操作则生成新版本。它能显著提升读多写少场景下的并发性能,同时在一定条件下维持可串行化或接近可串行化的效果。

4.4 乐观并发控制

乐观并发控制假设事务冲突较少,通常先并发执行,再在提交前统一验证是否存在冲突。如果验证失败,事务需要回滚重试。该方法在低冲突环境中效率较高,但在高竞争场景下可能产生较多重复计算。

5 数据库隔离级别

5.1 与读未提交的关系

读未提交是最低隔离级别,事务可能读到其他事务尚未提交的数据。相比之下,可串行化要求远高于这一水平,通常能够避免脏读等问题,因此两者在一致性保证上差异明显。

5.2 与读已提交的关系

读已提交确保事务只能看到已提交的数据,但并不自动保证同一事务内多次读取结果完全一致。可串行化比读已提交更严格,能够进一步限制事务交错带来的异常现象,使执行结果更接近某个明确的串行顺序。

5.3 与可重复读的关系

可重复读保证同一事务在重复读取同一数据时,不会看到前后不一致的值变化,但仍可能存在幻读等现象。可串行化通常比可重复读更严格,目标是消除更广泛的并发异常,而不仅仅是重复读不一致问题。

5.4 与最高隔离级别的关系

在许多数据库体系中,最高隔离级别通常以可串行化为目标或近似目标。实际实现可能通过锁、时间戳、快照或验证机制来逼近这一标准,但具体效果和性能代价会因系统设计而不同。

6 性能与代价

6.1 吞吐量影响

严格保证可串行化往往会减少事务之间的自由交错空间,从而限制并发度。系统为了维护正确性,可能需要让部分事务等待,这会在一定程度上影响整体吞吐量。

6.2 延迟与阻塞

当事务争用同一资源时,锁等待、冲突检测或验证失败都会增加单个事务的完成时间。对延迟敏感的应用而言,这种阻塞可能比吞吐量下降更值得关注。

6.3 死锁与回滚

采用锁机制时,事务之间可能因相互等待而产生死锁;采用乐观或时间戳机制时,则可能因冲突而频繁回滚。无论哪种方式,都意味着为维持正确性要付出额外代价。

6.4 资源消耗

实现可串行化通常需要维护锁表、版本链、时间戳信息或验证记录等辅助结构,这会带来额外内存与管理开销。事务规模越大、并发程度越高,这些资源成本越容易显现。

7 应用场景

7.1 关系型数据库

关系型数据库长期以来是可串行化研究和实现的主要场景。由于其事务模型清晰、数据关系明确,适合使用锁、版本控制和隔离级别等机制来保证并发一致性。

7.2 分布式数据库

在分布式环境中,事务可能跨节点访问多个分片,协调成本更高。此时可串行化的实现不仅要处理本地冲突,还要应对网络延迟、节点故障和一致性协调问题。

7.3 事务处理系统

订单处理、库存更新、账户变更等事务处理系统,对并发一致性要求较高。可串行化能够帮助系统避免重复扣减、超卖或状态错乱等问题,因此在这类场景中具有重要价值。

7.4 金融与订单一致性场景

在金融记账、支付结算、抢购下单等场景中,事务结果必须准确且可追溯。可串行化虽然可能带来更高的性能成本,但能显著降低并发异常导致的业务风险。

8 相关概念

8.1 原子性

原子性要求事务中的操作要么全部成功,要么全部失败。它关注的是“整体完成”或“整体不做”,与可串行化共同构成事务正确性的基础。

8.2 一致性

一致性指事务执行前后,数据库应满足既定约束和规则。可串行化有助于维持一致性,但一致性本身还依赖业务约束、完整性检查和应用逻辑。

8.3 隔离性

隔离性强调并发事务之间彼此不应相互干扰到产生错误结果。可串行化可以看作隔离性的一种强形式表达,是事务隔离目标的高级体现。

8.4 持久性

持久性保证一旦事务提交,其结果就不会因系统故障而丢失。它主要解决恢复与可靠性问题,与可串行化关注的并发正确性属于不同维度,但在数据库系统中同样重要。

8.5 线性化与可串行化的区别

线性化常用于并发对象或分布式系统的一致性描述,要求每个操作看起来都在某个瞬时点原子生效,并且满足实时顺序约束。可串行化则主要针对事务集合,关注多个事务整体结果是否等价于某个串行顺序。两者都强调“看起来像顺序执行”,但适用对象和约束条件并不相同。