分布式事务
XA 模型
概述
XA 模型是一种基于两阶段提交(2PC, Two-Phase Commit)的分布式事务协议。它由 X/Open 组织定义,旨在确保分布式系统中的多个资源管理器(如数据库、消息队列等)在事务提交时能够保持一致性。
两阶段提交(2PC)
准备阶段(Prepare Phase):
事务协调者(Transaction Coordinator)向所有参与者(Participants)发送准备请求,询问它们是否可以提交事务。
参与者执行事务操作,但不提交,而是将事务状态记录在日志中,并向协调者返回“准备就绪”或“无法准备”的响应。
提交阶段(Commit Phase):
如果所有参与者都返回“准备就绪”,协调者向所有参与者发送提交请求,参与者完成事务提交。
如果有任何一个参与者返回“无法准备”,协调者向所有参与者发送回滚请求,参与者回滚事务。
优点
强一致性:确保所有参与者要么全部提交,要么全部回滚,保证事务的强一致性。
标准化:XA 协议是标准化的,许多数据库和消息队列都支持 XA 协议。
缺点
性能问题:两阶段提交过程中,所有参与者都需要锁定资源,直到事务提交或回滚,可能导致性能瓶颈。
单点故障:事务协调者是单点故障点,如果协调者失败,整个事务可能无法继续。
TCC 模型
概述
TCC(Try-Confirm-Cancel)模型是一种基于补偿机制的分布式事务处理模型。它将事务分为三个阶段:Try、Confirm 和 Cancel。
三个阶段
Try 阶段:
尝试执行事务操作,但不提交,而是预留资源或执行部分操作。
如果所有参与者都成功完成 Try 阶段,则进入 Confirm 阶段;如果有任何一个参与者失败,则进入 Cancel 阶段。
Confirm 阶段:
确认事务操作,提交所有预留的资源或完成所有部分操作。
如果 Confirm 阶段成功,则事务完成;如果失败,则需要回滚。
Cancel 阶段:
取消事务操作,释放所有预留的资源或撤销所有部分操作。
确保事务回滚到初始状态。
优点
性能优化:TCC 模型在 Try 阶段不锁定资源,减少了资源锁定时间,提高了性能。
补偿逻辑可自定义:TCC 模型允许在 Confirm 和 Cancel 阶段编写自定义补偿操作,适用于复杂的业务场景。
缺点
复杂性:TCC 模型的实现较为复杂,需要开发者编写 Try、Confirm 和 Cancel 阶段的逻辑。
一致性问题:TCC 模型不能保证强一致性,可能会出现部分提交或部分回滚的情况。
Saga 模型
概述
Saga 模型是一种基于事件驱动的分布式事务处理模型。它将一个长事务分解为多个短事务,每个短事务都有对应的补偿操作。
执行流程
正向操作:
按顺序执行一系列短事务操作,每个操作都有对应的补偿操作。
如果所有操作都成功,则事务完成。
补偿操作:
如果某个操作失败,则按相反顺序执行补偿操作,撤销之前已完成的操作。
确保事务回滚到初始状态。
优点
高可用性:Saga 模型通过事件驱动的方式,减少了资源锁定时间,提高了系统的可用性。
补偿逻辑可自定义:Saga 模型允许在每个短事务中定义补偿操作,适用于复杂的业务场景。
缺点
最终一致性:Saga 模型不能保证强一致性,只能保证最终一致性。
复杂性:Saga 模型的实现较为复杂,需要开发者编写每个短事务及其补偿操作的逻辑。
三种模型的适用场景
XA 模型:适用于需要强一致性的场景,但性能和可用性较差。
TCC 模型:适用于性能要求较高且业务逻辑复杂的场景,但实现复杂。
Saga 模型:适用于高可用性和最终一致性的场景,但实现复杂且不能保证强一致性。
安全性与活性:2PC 的问题出在哪
前面三节把 XA / TCC / Saga 并列讲了,但要判断一个协议的好坏需要一把尺子。分布式算法有两条性质:
- 安全性(safety):坏事不会发生;
- 活性(liveness):好事最终会发生。
2PC 是安全的(不会把坏数据写进数据库),但它的活性不好:TM 在错误的时点失败,整个事务就阻塞。这是 2PC 最被人诟病的地方,也是后面三节要解决的对象。
2PC 的出处与「必然要三段」这个结论
- 2PC 由 Jim Gray 在 Notes on Database Operating Systems(1979)里描述。
- Skeen 在 NonBlocking Commit Protocols(1981)里证明:分布式事务要避免 2PC 的阻塞问题,需要三段提交(3PC)。必要性得到了证明,但一个可用的 3PC 算法迟迟没有出现 —— 用该文的说法,这件事「花了将近 25 年」。
这条结论把问题定了性:阻塞的根源在两阶段的形态本身,不在 2PC 实现得好不好。
事务提交不是拜占庭问题
1986 年,做共识的人和做事务的人开了一次会。当时共识侧最好的算法是拜占庭将军方案,但它对事务来说太贵。Jim Gray 把会议整理成 A Comparison of the Byzantine Agreement Problem and the Transaction Commit Problem(1987),引言里写道:
Prior to the conference, it was widely believed that the transaction commit problem faced by distributed systems is a degenerate form of the Byzantine Generals Problem studied by academe. Perhaps the most useful consequence of the conference was to show that these two problems have little in common.
也就是说:在会议之前大家以为事务提交是拜占庭问题的退化情形,会后发现两者几乎没有共同点。
而事务提交真正对应的共识变种是 uniform consensus(一致共识):所有进程都必须就取值达成一致,包括故障的那些。理由很直接 —— 事务只有在所有 RM 都 prepared 的前提下才允许提交。这与「只要非故障进程达成一致」的普通共识不同,uniform consensus 比普通共识更难(见 Uniform consensus is harder than consensus,2000)。
Paxos 的安全性来自一个简单的交集论证
Paxos 的多数派要求(majority quorum)常被当成一条规则背下来,它背后的推理只有一句话:给定固定的进程数,任意两个多数派必然至少有一个共同进程。
以三个进程 A、B、C 为例,可能的多数派只有 AB、AC、BC 三种。假设在 AB 在场时做出了一个决定,那么将来任意一个多数派在场时,至少有一个进程记得上次的决定:
| 将来在场的多数派 | 谁记得上次的决定 |
|---|---|
| AB | A 与 B 都记得 |
| AC | A 记得 |
| BC | B 记得 |
于是旧的决定不可能被后来的多数派推翻。 这就是 Paxos 安全性的来源,也是它不需要依赖时钟的原因。
Paxos 能容忍的故障形态(按该文的列举):消息丢失、延迟、重复、乱序。达成共识的条件是:存在单个 leader 持续足够久,使它能与多数派通信两次。容错范围还包括:
- 任何进程(包括 leader)都可以失败并重启;
- 所有进程同时失败过,算法仍然是安全的;
- 可以同时存在多个 leader(安全性不受影响,只影响活性)。
活性是有条件的:Paxos 是异步算法,没有显式超时,它只在系统表现得同步(消息在有界时间内送达)时才达成共识;否则保持安全但不做决定。存在符合 FLP 结论的病理性场景,但那类场景在实践中容易避开。
Paxos Commit:用 Paxos 让 2PC 的 TM 容错
Lamport 与 Gray 在 Consensus on Transaction Commit(2005)里把 Paxos 用到事务提交上。做法是:
- 用 Paxos 复制 2PC 的 TM(TM 不再是单点);
- 为每一个参与事务的 RM 起一个 Paxos 实例,用来就「该 RM 能否提交」达成一致。
「每个 RM 一个 Paxos 实例」看起来开销很大,但该文给的计算是:在无故障情形下 Paxos Commit 两个阶段就完成,消息延迟与 2PC 相同(只是消息条数更多)。只有在出现故障时才需要第三阶段 —— 这与上面 Skeen 的结论一致。
容错界是:给定
有一处容易误解的地方值得单独指出:Paxos Commit 并没有用 Paxos 去直接解 uniform consensus,它是用 Paxos 让系统容错。
那它到底还阻不阻塞:一场带数字的具体争论
这是这一篇材料里最有价值的部分 —— 博客评论区里博主与读者就 Paxos Commit 是否仍然阻塞展开了来回,双方都承认了一部分。
- 博主的断言:「因为 2PC 会阻塞所以分布式事务不该用」是个空洞的论点,Paxos Commit 已经处理了阻塞问题。
- 读者的反驳:Paxos Commit 仍然是阻塞协议。若一个 RM 投了 prepare,而网络故障、或 leader 长时间无法与多数派通信两次,这个 RM 会处于阻塞状态。
- 博主承认的两种阻塞条件:① 可用 acceptor 不足多数派;② 有两个活跃 leader 试图选出不同结果。他同时指出第二种不太可能,因为 Paxos Commit 里任何新 leader 都会试图让 Abort 胜出。
- 读者补的第三种:RM 投出 yes 之后,若网络断到无法与任何 acceptor 通信,它会无限期不知道事务到底提交还是中止。
争议的落点不在概率,而在状态:prepared 状态下资源提供方可能无限期等待结果,这件事在跨管理域、跨信任域时尤其要紧。
博主随后给了量化:5 个 acceptor、平均无故障时间 120 天、平均修复时间 1 天,则平均到 Paxos 发生阻塞的时间是 160 年。 他也立刻补了一句:这个值是否真的能达到、需要什么条件才达到,才是关键问题。
以上出自 2007 年的一篇博客及其评论区(Mark Mc Keown, Beta Thoughts),未经同行评审。引用到的文献是真实的经典文献,但转述与推理过程未经评审,采信时需要知道这一点。评论区里「160 年」这类数字是博主的估算,文中未给出推导。
用共识视角看 CAP
同一篇材料给了一个把 CAP 与共识直接对上的推法,值得记下来作对照(CAP 定理 那篇从 Gilbert-Lynch 的形式化走,这里从 FLP 走):
- 把 consistency 等同于 consensus;
- 异步系统加一个故障进程 → 由 FLP 可知无法达成共识;
- 于是异步系统下不可能同时具备一致性与可用性。
再用三节点的 Paxos 系统 A、B、C 具体看分区:
- 两个节点可用即可达成共识 —— 也就是一致性与可用性都有;
- 若 C 被分区,C 无法响应。原因是 C 分不清三种情况:自己被分区了、另外两个节点宕了、还是网络只是很慢;
- 另外两个节点仍是多数派,可以继续服务。
所以要绕开分区带来的不可用,只能靠工程手段:
- 数据中心内部:用两张相互独立的网络 —— Paxos 不在意消息重复,所以多路径是安全的;
- 互联网上:让客户端同时查询全部节点,C 不通就查 A 或 B;
- 同步网络下另有一条路:C 若在固定时间内收不到消息,可以判定自己被分区,于是主动向客户端声明自己已宕机。
最后这条与 CAP 定理 那篇里 Brewer 的说法正好对上 —— 「检测分区、进入显式的分区模式」是同步模型给的能力,纯异步模型没有。
相关
- 01-CAP 定理 —— 本篇「用共识视角看 CAP」一节用的是那篇的结论
- 02-BASE 理论 —— 两种取舍口径同源:一个在定理层,一个在模式层
- 03-一致性模型 —— 事务隔离(可串行化)与一致性模型是两个正交的轴
参考
- Mark Mc Keown. A brief history of Consensus, 2PC and Transaction Commit. Beta Thoughts, 2007-06-13.
- Jim Gray. Notes on Database Operating Systems. 1979.
- Dale Skeen. NonBlocking Commit Protocols. SIGMOD 1981.
- Jim Gray. A Comparison of the Byzantine Agreement Problem and the Transaction Commit Problem. 1987.
- Leslie Lamport, Jim Gray. Consensus on Transaction Commit. ACM TODS 2006(2005 年提交).
- Bernadette Charron-Bost, André Schiper. Uniform consensus is harder than consensus. 2000.
YJ