第一部分:非阻塞提交的起源:从两阶段提交(2PC)的缺陷到三阶段提交(3PC)的构想
在分布式计算领域,确保跨多个独立节点执行的事务的完整性是一项至关重要的挑战。这些节点或站点可能在地理上分散,并通过可能出现故障的网络连接。在这样的环境中,一个核心的要求是维持事务的原子性(Atomicity),这是ACID属性的基石。
1.1 分布式系统中的原子性指令
分布式事务涉及在多个站点(节点)上执行的一系列操作,这些操作必须被视为一个单一的、不可分割的工作单元 1。原子性这一属性确保了一个事务要么在所有参与站点上完全成功(提交),要么在所有站点上完全失败(中止)4。出现一个站点提交而另一个站点中止的情况是不可接受的,因为它会破坏分布式数据库的整体一致性和数据完整性 1。原子提交协议(Atomic Commit Protocols, ACPs),如两阶段提交(2PC)和三阶段提交(3PC),正是为了强制执行这种“全有或全无”的保证而设计的 4。
1.2 两阶段提交(2PC)协议:简要回顾
两阶段提交协议是实现分布式事务原子性的经典方法,其结构围绕一个中心**协调者(Coordinator)和多个参与者(Participants)**展开 7。
阶段一:投票/准备阶段(Voting/Prepare Phase)
协调者向所有参与者发送一个 VOTE-REQ 或 PREPARE 消息,询问它们是否能够提交事务 2。每个参与者在收到请求后,会检查其本地是否可以执行并提交该事务。如果可以,它会将事务执行所需的所有信息(如重做和撤销日志)写入稳定存储(如磁盘),锁定相关资源,然后向协调者回复
VOTE-COMMIT
或Yes
9。如果由于任何原因无法提交,它将回复VOTE-ABORT
或No
。至关重要的是,一旦参与者投票Yes
,它就做出了一个有约束力的承诺:它不能再单方面中止事务,必须等待协调者的最终指令 11。阶段二:提交/中止阶段(Commit/Abort Phase)
协调者收集所有参与者的投票。如果它收到了所有参与者的 Yes 投票,它就会在其日志中记录一个全局 COMMIT 的决定,并向所有参与者发送 GLOBAL-COMMIT 消息。反之,只要有任何一个参与者投票 No 或在超时期限内未响应,协调者就会决定中止事务,并向所有参与者发送 GLOBAL-ABORT 消息 2。参与者在收到最终决定后,会相应地完成事务(提交或中止),并释放它们在准备阶段持有的锁和资源 10。
1.3 2PC的根本性阻塞问题
尽管2PC在理想情况下能够保证原子性,但其核心弱点在于它是一个阻塞协议(blocking protocol) 13。这种阻塞并非简单的性能下降,而是一种可能导致整个系统部分或全部停滞的严重问题。
关键的失败场景
阻塞问题在以下特定场景中暴露无遗:当协调者在接收到所有参与者的 Yes 投票(此时所有参与者都进入了“准备就绪”或“不确定”状态)之后,但在成功地将最终的 COMMIT 或 ABORT 决定传达给所有参与者之前发生崩溃 9。
无限期的僵局
在这种状态下,所有幸存的参与者都会被阻塞。它们陷入了一个两难的境地:
它们不能单方面决定中止,因为其他某个参与者(或者协调者在崩溃前)可能已经收到了
COMMIT
消息并完成了提交。如果此时中止,将导致数据不一致 9。它们也不能单方面决定提交,因为协调者可能在收集完所有 Yes 投票后,由于某种原因(例如,它自己的日志写入失败或一个参与者的 Yes 投票信息在网络中丢失)决定了中止事务。
因此,参与者们别无选择,只能无限期地等待协调者恢复,并由其重新分发最终的事务决议 14。
资源瘫痪
在被阻塞期间,参与者会继续持有为该事务所获取的关键数据库锁和其他系统资源。这意味着这些资源对于任何其他事务都是不可用的,从而可能引发连锁反应,导致更大范围的系统停顿 12。这种严重的可用性问题,正是催生像3PC这样的非阻塞协议的主要动机 6。
“阻塞”一词在2PC的语境下,其严重性远超字面含义。它不仅仅是单个事务的延迟,而是一种持有资源的死锁状态,这种状态会迅速蔓延。在一个高并发的系统中,比如金融交易系统,一个被阻塞的参与者持有的锁会阻止其他无关的事务访问相同的数据,从而形成一个不断增长的阻塞进程队列 17。这直接转化为业务交易的失败、服务水平协议(SLA)的违背,以及需要昂贵且易错的人工干预来解决状态僵局。这种人工干预,即所谓的“启发式决策”(heuristic decision),本身就带来了破坏数据一致性的风险 14。因此,设计一个能够自主恢复并释放资源而无需人工介入的协议,成为了分布式系统领域一个迫切的需求。3PC正是为了应对这一挑战而诞生的。
第二部分:三阶段提交协议的理论基础
为了克服2PC的阻塞缺陷,研究人员开始探索新的协议设计。其中,由戴尔·斯基恩(Dale Skeen)提出的三阶段提交协议,从理论上为构建非阻塞的原子提交协议奠定了基础。
2.1 历史背景与斯基恩的贡献
三阶段提交协议由戴尔·斯基恩在其1981年发表的里程碑式论文《非阻塞提交协议》("Nonblocking commit protocols")中正式提出 19。斯基恩是第一个明确认识到需要一个三阶段协议来避免在任意单点故障下发生阻塞的学者 22。他的工作创新性地使用有限状态自动机(finite state automata)来对提交协议进行建模,从而能够形式化地推理其正确性和容错属性 20。
2.2 核心创新:PreCommit
(预提交)状态
3PC的核心创新在于,它在2PC的两个阶段之间插入了一个额外的“缓冲”阶段,即预提交阶段(Pre-Commit Phase) 6。这个新增阶段的根本目的,是将2PC中那个单一且模糊的
prepared
(准备就绪)状态,分解为两个界限分明、含义确切的状态:waiting
(等待,或称 ready
)和 pre-commit
(预提交)6。
这种状态分解的精妙之处在于,它确保了当一个参与者处于某个特定状态时,其未来可能达到的最终结果(提交或中止)集合是确定的且受限的。具体来说:
从
pre-commit
状态出发,唯一可能的最终状态是commit
。从
waiting
状态出发,如果发生故障(如协调者超时),最终状态将是abort
8。
2.3 形式化的非阻塞属性
斯基恩的论文为“非阻塞”这一特性提供了严格的形式化定义。一个协议是非阻塞的,当且仅当它满足以下两个条件 25:
不存在任何一个本地状态
s
,从该状态出发,既可能转换到commit
状态,也可能转换到abort
状态。不存在任何一个“非可提交”状态(non-committable state)
s
,从该状态出发,可能转换到commit
状态。所谓“可提交状态”(committable state),是指所有站点都已经投票同意提交事务的状态。
3PC的设计严格遵循了这一定理。waiting
状态与 abort
状态相邻(通过超时触发),但与 commit
状态不直接相邻(必须先进入 pre-commit
状态)。而 pre-commit
状态是一个可提交状态,它与 commit
状态相邻,但(在没有网络分区的情况下)与 abort
状态不相邻 8。这种状态机的结构保证了,只要有幸存的、可通信的站点,它们就永远不需要因为等待一个故障站点的恢复而被阻塞,因为幸存者群体的集体状态已经包含了足够的信息来做出一个全局一致的决定 6。
在事务处理中,“提交点”(commit point)是“不可逆转点”(point of no return)17。在2PC中,这个提交点是一个脆弱的、集中的时刻:即协调者在其日志中写入
COMMIT
记录的那一刻。如果它在此之后立即崩溃,只有恢复后的协调者自己知道最终的决议,从而导致其他所有参与者阻塞。
3PC通过引入 PreCommit
阶段,从根本上改变了这一模式。它将“提交点”从一个集中的、单一的事件,转变为一个分布式的、容错的状态。当协调者收到所有 Yes
投票后,它做出了提交的决定。但它并不立即执行,而是通过 PreCommit
消息将这个意图广播出去。当一个参与者进入 PreCommit
状态时,它就确切地知道:所有其他参与者必定已经投票同意了。这种在参与者之间共享的知识,构成了一种关于“提交必然性”的分布式共识,即使最终的 DoCommit
消息尚未到达。通过将决策与最终的执行解耦,3PC实现了其非阻塞设计的核心思想。
第三部分:3PC协议机制深度剖析
本节将详细、逐步地拆解3PC协议的执行流程,阐明协调者和参与者的状态、消息传递以及在每个阶段的具体行为。
3.1 协议角色与状态定义
协调者(Coordinator):作为主控进程,负责驱动整个事务协议的流程 8。
状态集:
Initial
(初始)、Waiting
(等待投票)、PreCommit
(预提交)、Committed
(已提交)、Aborted
(已中止)。
参与者(Participant):作为从属进程,负责执行事务的本地部分,并根据协调者的指令行动 8。
状态集:
Initial
(初始)、Waiting
(或Ready
,已投票等待)、PreCommit
(预提交)、Committed
(已提交)、Aborted
(已中止)8。
3.2 阶段一:投票阶段 (CanCommit?
)
协调者动作:当应用程序请求提交事务时,协调者向所有参与者发送
CanCommit?
(或VOTE-REQ
)消息,然后自身转换到Waiting
状态,等待参与者的响应 7。参与者动作:收到
CanCommit?
消息后,参与者检查本地资源和约束,判断是否能够提交事务。它会获取必要的资源锁,并将准备信息写入本地事务日志 8。如果可以提交,它向协调者回复
Yes
(或VOTE-COMMIT
),并进入Waiting
/Ready
状态 26。如果不能提交,它回复
No
(或VOTE-ABORT
),并立即进入Aborted
状态,因为单个No
票就足以中止整个事务 26。
3.3 阶段二:预提交阶段 (PreCommit
)
这是3PC区别于2PC的关键阶段。
协调者动作:
如果在
Waiting
状态下收到任何一个参与者的No
回复,或者等待某个参与者回复超时,协调者将向所有参与者发送Global-Abort
消息,并进入Aborted
状态 7。如果协调者从所有参与者那里都收到了
Yes
回复,它就做出了全局提交的决定。此时,它向所有参与者发送PreCommit
消息,然后自身转换到PreCommit
状态 7。
参与者动作:当处于
Waiting
/Ready
状态的参与者收到PreCommit
消息时,它就确信所有其他参与者都已同意提交。它会将PreCommit
状态持久化到日志中,向协调者发送一个ACK
(确认)消息,并转换到自身的PreCommit
状态 26。在此状态下,它已准备好提交,但仍未执行任何不可撤销的操作 7。
3.4 阶段三:最终化阶段 (DoCommit
)
协调者动作:在
PreCommit
状态下,协调者等待所有参与者的ACK
消息。一旦收到所有ACK
,它就确认提交的决定已在整个系统中安全记录。此时,协调者进入Committed
状态,记录最终提交日志,并向所有参与者发送DoCommit
(或Global-Commit
)消息 8。参与者动作:当处于
PreCommit
状态的参与者收到DoCommit
消息时,它将最终执行事务(使更新永久生效),释放之前获取的锁和资源,并转换到Committed
状态 8。
表1:3PC协议详细状态转换逻辑
为了清晰地呈现3PC协议中复杂的、基于规则的逻辑,下表综合了多个来源的信息 8,提供了一个权威的状态转换视图。
角色 | 当前状态 | 事件 (消息/超时) | 动作 | 新状态 |
协调者 | Initial | 应用程序请求提交 | 向所有参与者发送 CanCommit? | Waiting |
参与者 | Initial | 收到 CanCommit? | 记录 prepare 日志, 锁定资源, 发送 Yes | Waiting |
参与者 | Initial | 收到 CanCommit? (无法提交) | 发送 No | Aborted |
协调者 | Waiting | 收到所有参与者的 Yes | 向所有参与者发送 PreCommit | PreCommit |
协调者 | Waiting | 收到任何 No 或超时 | 向所有参与者发送 Global-Abort | Aborted |
参与者 | Waiting | 收到 PreCommit | 记录 pre-commit 日志, 发送 ACK | PreCommit |
参与者 | Waiting | 收到 Global-Abort 或超时 | 中止事务, 释放锁 | Aborted |
协调者 | PreCommit | 收到所有参与者的 ACK | 向所有参与者发送 DoCommit | Committed |
协调者 | PreCommit | 等待 ACK 超时 | 向所有参与者发送 Global-Abort | Aborted |
参与者 | PreCommit | 收到 DoCommit | 提交事务, 释放锁 | Committed |
参与者 | PreCommit | 等待 DoCommit 超时 | 启动恢复协议 | 根据恢复结果而定 |
第四部分:容错与恢复场景分析(故障-停止模型)
本节将分析3PC协议如何在“故障-停止”(fail-stop)模型下通过处理节点故障来实现其非阻塞特性。该模型假设节点只会因崩溃而失效,并且网络通信是可靠的,不会发生分区 7。
4.1 处理协调者故障
故障检测:参与者通过超时机制检测协调者的故障。如果一个参与者处于
Waiting
或PreCommit
状态,并且在预设的时间内没有收到预期的下一条消息(分别是PreCommit
或DoCommit
),它就会假定协调者已经崩溃 7。恢复协议:幸存的参与者们会启动一个恢复协议。这通常涉及从它们中间选举一个新的恢复协调者(recovery coordinator)。选举本身的机制是一个独立的协议,例如可以基于节点ID的大小来决定 8。
恢复协调者的决策逻辑:新的协调者通过查询所有其他可用参与者的状态来确定事务的最终命运 7。其决策逻辑如下:
如果查询到任何一个参与者处于
Committed
状态:新协调者可以断定,原协调者在崩溃前已经发出了DoCommit
指令。因此,它可以安全地向所有参与者广播DoCommit
消息,以确保所有节点都完成提交。如果查询到有参与者处于
PreCommit
状态(但没有节点处于Committed
状态):新协调者可以推断,原协调者成功完成了第一阶段(所有人都投了Yes
)并启动了第二阶段。这意味着全局的意图是提交。因此,它可以安全地接管并推动事务完成。它会重新发送PreCommit
消息给可能没收到的节点,等待所有节点的ACK
,然后发送DoCommit
7。如果所有可达的参与者都处于
Waiting
状态:新协调者可以确定,原协调者不可能已经发出了PreCommit
消息(否则至少会有一个参与者处于PreCommit
状态)。既然没有进入预提交阶段,那么全局提交的决定就不可能做出。因此,它可以安全地向所有参与者发送Global-Abort
消息 8。如果查询到任何一个参与者处于
Aborted
状态:这意味着事务必须中止。新协调者会向所有参与者广播Global-Abort
消息。
4.2 处理参与者故障
故障检测:协调者通过超时机制检测参与者的故障,例如在等待
Yes
/No
投票或等待PreCommit
的ACK
时 7。协调者动作:
如果在
Waiting
状态下(等待投票)发生参与者超时,协调者尚未做出提交决定。它会向所有其他幸存的参与者发送Global-Abort
指令 7。如果在
PreCommit
状态下(等待ACK)发生参与者超时,情况则更为复杂。此时,协调者已经向所有参与者发送了PreCommit
消息。虽然它知道所有参与者都同意提交,但某个参与者未能确认其已进入PreCommit
状态,这带来了不确定性。为了保证原子性,最保守也是最安全的选择是中止整个事务。因此,协调者会向所有幸存者发送Global-Abort
指令 8。
4.3 恢复的不对称性与“故障-恢复”的脆弱性
标准的3PC恢复协议在纯粹的“故障-停止”模型下运行良好,但它对于“故障-恢复”(fail-recover)场景却异常脆弱。在这种场景中,原协调者崩溃后又重新上线,而此时一个恢复协调者已经接管了工作。这可能在没有网络分区的情况下导致“脑裂”(split-brain)问题,从而破坏一致性。
这个微妙但致命的场景可以这样展开 7:
原协调者C1发送
PreCommit
消息后,在收到所有ACK
之前崩溃。幸存的参与者超时后,选举出一个新的恢复协调者C2。
C2查询参与者状态,发现有节点处于
PreCommit
状态,于是开始引导事务走向Commit
状态。几乎在同一时间,原协调者C1恢复了。它并不知道C2的存在。它检查自己的状态,发现自己仍在等待某些
PreCommit
的ACK
。C1等待超时后,按照其协议逻辑,决定中止事务,并向所有参与者发送
Global-Abort
消息。结果,一些参与者可能从C2那里收到
DoCommit
消息,而另一些参与者则从C1那里收到Global-Abort
消息,导致最终状态不一致。
这个例子揭示了3PC的正确性在很大程度上依赖于一个干净的“故障-停止”模型和一个强大的机制来防止“僵尸协调者”(zombie coordinator)的出现。这种额外的复杂性,是其在工程实践中难以被正确实现和部署的重要原因之一。
第五部分:致命缺陷:3PC对网络分区的脆弱性
这是本报告最关键的部分,它详细阐述了为何3PC尽管解决了2PC的阻塞问题,却未能在实际系统中得到应用。其非阻塞的保证是以在网络分区下牺牲原子性为代价的。
5.1 超越故障-停止模型:异步网络
原始的3PC协议是在一个同步网络模型的假设下设计的,该模型认为超时可以可靠地指示节点崩溃 7。然而,现实世界中的网络本质上是异步的。一次超时事件的含义是模糊的:它可能意味着对方节点已经崩溃,也可能意味着网络链路中断(即网络分区),或者仅仅是消息被严重延迟 1。3PC协议对于网络分区是
不具弹性的,这是其致命缺陷 7。
5.2 “脑裂”不一致场景
网络分区(network partition)可以将系统中的节点分割成两个或多个相互隔离的组,这些组内部的节点可以正常通信,但组与组之间无法通信 1。这种情况可能导致“脑裂”(split-brain),即不同的分区对同一个事务做出了相互冲突的决定 32。
以下是导致数据不一致的详细场景演练:
初始设置:一个协调者(C)和四个参与者(P1, P2, P3, P4)。
阶段一和阶段二开始:C发送
CanCommit?
,所有参与者回复Yes
。随后,C向所有参与者发送PreCommit
消息。网络分区发生:在所有
PreCommit
消息到达或被确认之前,网络发生分区,将节点分为两个隔离的组:分区A = {C, P1, P2} 和分区B = {P3, P4}。我们假设P1和P2成功收到了PreCommit
消息,而P3和P4没有。分区A的决策(提交):在分区A内部,C可能收到了P1和P2的
ACK
。此时,C等待P3和P4的ACK
超时。根据某些3PC变种的实现,如果一个分区内包含了协调者和多数参与者,它可能会继续推进。为了说明问题,我们假设分区A的协议(由C领导)最终决定提交。于是,C、P1和P2提交了事务。分区B的决策(中止):在分区B内部,参与者P3和P4等待C的
PreCommit
消息超时。它们假定C已经失败,于是启动恢复协议,选举出一个新的恢复协调者(例如P3)。P3查询P4的状态,发现其处于Waiting
状态。由于在它们这个分区内,没有任何参与者进入了PreCommit
状态,P3根据其有限的世界观,正确地推断出原协调者不可能做出提交决定,因此事务应该中止。于是,P3和P4中止了事务 7。不一致的后果:一段时间后,网络分区愈合。此时,系统已经处于一个严重不一致的状态:P1和P2已经提交了事务,而P3和P4却中止了同一个事务。这直接违反了原子性,而原子性正是该协议本应保证的核心属性 7。
5.3 失败分析
PreCommit
状态之所以无法解决网络分区问题,是因为每个分区内的决策过程都是基于不完整的信息。分区B中的恢复协议,根据其所能观察到的信息做出了“正确”的本地决策,但它的信息是过时且不完整的。
3PC的非阻塞特性恰恰是导致这种失败的根源。它允许一个节点子集为了避免阻塞而做出决策,但由于缺乏一个能够确保该决策全局一致的机制,它为了活性(liveness)而牺牲了一致性(consistency)26。
5.4 CAP定理的实践例证
3PC在网络分区期间的失败,是CAP定理的一个经典教科书式例证。通过在分区期间试图保持可用(即非阻塞),它牺牲了系统的一致性。
CAP定理指出,任何一个分布式系统在以下三个保证中,最多只能同时满足两个:一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)17。当网络分区(P)发生时,系统设计者必须在C和A之间做出选择。
2PC的选择:2PC选择了一致性。它通过阻塞(牺牲可用性)来避免在分区期间做出可能导致不一致的决策。
3PC的选择:3PC试图在分区期间保持非阻塞(追求可用性),但这允许了不同分区独立推进并做出决策,最终导致了不一致的结果,即牺牲了一致性。
这就是为什么3PC虽然在理论上解决了2PC的阻塞问题,但在实践中却被认为是不可用的。对于要求数据强一致性的原子提交场景,任何牺牲一致性的行为都是不可接受的。
第六部分:比较分析与向现代共识的演进
本节将3PC置于更广阔的分布式协议领域中,将其与2PC进行直接对比,并解释为何现代系统已经转向了如Paxos和Raft等更为强大的解决方案。
6.1 3PC vs. 2PC:明确的权衡分析
3PC的优势:在故障-停止模型下,它对于协调者或参与者的单点故障是非阻塞的,解决了2PC最主要的痛点 6。
3PC的劣势:
更高的延迟和开销:即使在没有任何故障的“晴天”场景下,3PC也需要额外一轮的消息交互(
PreCommit
和ACK
),这增加了每个事务的延迟 7。更高的复杂性:协议的逻辑,特别是涉及超时和选举的恢复部分,在实现和维护上要复杂得多 34。
在网络分区下失效:如前所述,它无法在网络分区的情况下保证原子性,这对任何严肃的分布式系统来说都是一个致命的缺陷 15。
6.2 范式转移:Paxos和Raft
现代的容错分布式系统,绝大多数都选择了像Paxos及其更易于理解的变体Raft这样的共识算法,而不是3PC 13。这些是通用的共识算法,旨在让一组节点能够在出现包括网络分区在内的各种故障时,就某个值(例如,事务的最终结果)达成一致 36。
6.3 法定人数原则:为何Paxos/Raft成功而3PC失败
核心差异:与通常需要全体一致同意的2PC/3PC不同,Paxos和Raft基于**多数派投票(majority vote)或法定人数(quorum)**的原则来运作 18。
防止脑裂:一个决议只有在被多数节点接受后才能被最终确定。法定人数的数学特性保证了任意两个多数派集合(quorum)之间至少会有一个共同的节点。这个重叠的节点充当了信息传递的桥梁,从根本上防止了两个被隔离的分区做出相互冲突的决定 39。如果发生网络分区,且没有任何一个分区能够包含多数节点,那么系统将安全地阻塞(选择一致性而非可用性),但绝不会出现不一致的状态。与3PC那种可能导致数据损坏的“不安全”推进相比,这是一种安全且可预测的行为。
6.4 案例研究 - Google Spanner:现代方法
像Google Spanner这样的生产级全球分布式数据库,展示了解决分布式事务问题的最前沿方案 40。Spanner并
没有使用3PC。取而代之的是,它将2PC协议构建在Paxos共识算法之上 40。
架构:在Spanner中,一个2PC事务的“协调者”和“参与者”不再是单个服务器,而是本身就由Paxos管理的复制状态机(replicated state machines)。每个组件(协调者或参与者)都是一个由多个副本组成的Paxos组 41。
优势:这种复合架构集两家之长。它利用2PC相对简单的逻辑来协调跨越不同数据分片(shards)的事务。同时,它利用Paxos来确保事务中的每个组件都具有高可用性和分区容错性。如果一个充当协调者领导者(leader)的节点失败,其所在的Paxos组会迅速选举出一个新的领导者,2PC协议可以继续进行,从而解决了2PC的阻塞问题,同时又避免了3PC在网络分区下的不一致风险 40。
表2:原子提交与共识协议的比较分析
下表为一个高层次的战略总结,旨在帮助架构师和决策者理解不同协议之间的核心权衡。
特性 | 两阶段提交 (2PC) | 三阶段提交 (3PC) | Paxos / Raft |
消息轮次 | 2 | 3+ | 2+ (在基于领导者的变体中) |
延迟 | 较低 | 较高 | 较高 |
协调者故障时是否阻塞? | 是 (无限期阻塞) | 否 (在故障-停止模型中) | 否 (只要多数派存活) |
是否容忍网络分区? | 否 (会阻塞) | 否 (会产生不一致) | 是 (保持一致性) |
复杂性 | 中等 | 高 | 非常高 |
实践应用 | 广泛使用,常与共识算法结合 (如Spanner) | 实践中不使用 | 工业界共识标准 |
第七部分:结论:3PC的理论遗产与实践的无关性
本报告的最后部分将综合所有分析,为三阶段提交协议下一个明确的定论。
7.1 一个具有学术重要性的协议
3PC在分布式系统理论史上占有重要地位。它是第一个为创建非阻塞原子提交协议所做的严谨尝试,并为分析此类协议提供了形式化的状态机框架 20。学习3PC对于理解阻塞、活性和一致性之间微妙的关系至关重要,它也是理解为何需要像Paxos这样更强大协议的一个完美的教学跳板 4。
7.2 为何3PC在实践中不被使用
结论是明确且不容置疑的:3PC在现代生产系统中不被使用 4。
最主要的原因是它在网络分区下的灾难性故障模式。对于任何要求高可靠性的系统来说,数据损坏和状态不一致的风险都是不可接受的 15。
次要原因包括其相对于2PC更高的延迟和消息开销,以及显著增加的实现复杂性 34。它所提供的有限好处(在受限的故障-停止模型中解决阻塞问题)完全无法抵消其带来的巨大风险和成本。
7.3 最终裁决:一块有益的垫脚石
总而言之,3PC应被理解为分布式共识发展道路上一个重要但最终被证明存在缺陷的演化步骤。工业界已经转向了更健壮、数学上更完备的解决方案,主要是通过将2PC的事务逻辑与Paxos/Raft的分区容错共识保证相结合。3PC作为一个案例研究,仍然是揭示分布式系统设计中那些微妙且往往反直觉的挑战的宝贵教材。