在现代分布式系统中,数据往往分散在多个节点或数据库中,如何确保这些数据操作的一致性和可靠性,成为了一个核心挑战。这就是分布式事务要解决的问题。不同于单机事务,分布式事务跨越多个系统,需要协调各方参与者来保证操作的原子性。无论是电商平台的订单支付,还是金融系统的转账,都离不开分布式事务的支持。
本文将从基础概念入手,逐步剖析其原理、常见协议,帮助你构建一个完整的知识框架。
一、什么是分布式事务?
简单来说,分布式事务是一组操作,这些操作分布在两个或多个数据存储系统上,但必须作为一个整体成功或失败。
为什么需要分布式事务 ?举个栗子,在微服务架构盛行的今天,服务拆分导致数据分散。传统的本地事务只能处理单一数据库,无法跨系统协调,你有一个电商系统,下单功能横跨优惠券、订单、支付、消息、物流等多个服务,显然只有本地事务是不够的。
这时候,分布式事务就登场了,它确保了 ACID 属性在分布式环境下的延伸:
- Atomicity(原子性):所有操作要么全成功,要么全失败。
- Consistency(一致性):事务前后,数据从一个一致状态到另一个一致状态。
- Isolation(隔离性):并发事务互不干扰。
- Durability(持久性):一旦提交,变化永久保存。
分布式事务通常涉及多个节点,通过网络通信协调。

二、分布式事务的常见理论模型
要实现分布式事务,需要一套协议来协调各方。以下是几种经典方法,从简单到复杂逐步展开。
两阶段提交(2PC)
两阶段提交(Two-Phase Commit,2PC)是最早也是最常见的协议。 它分为准备阶段(Prepare)和提交阶段(Commit):
- 准备阶段:事务协调器(Coordinator)向所有参与者(Participants)发送"准备"请求。每个参与者执行本地操作,但不提交,而是回复"Yes"(准备好)或"No"(失败)。
- 提交阶段:如果所有参与者都回复"Yes",协调器发送"Commit"指令,大家提交;否则,发送"Rollback",大家回滚。
2PC 简单有效,通过协调者控制和多个参与者控制事务的原子性,但有缺陷:如果协调器崩溃,系统可能阻塞。此外,网络延迟会放大性能瓶颈。
下图展示了 2PC 的流程:

三阶段提交(3PC)
为了解决 2PC 对协调者的强依赖和阻塞等待问题,三阶段提交引入了Pre-Commit阶段,参与者在 PreCommit 后若长时间收不到 DoCommit,自动提交;
- CanCommit:协调器询问参与者是否能执行事务。
- PreCommit:如果全同意,参与者执行操作并锁定资源,但不提交。
- DoCommit:协调器决定提交或中止。
3PC 通过超时机制减少阻塞,但增加了通信开销,在实践中使用较少。
Saga 模式
在微服务时代,Saga 模式更受欢迎。它不是一个单一协议,而是一种补偿机制。 Saga 将长事务拆分成多个本地子事务,每个子事务有对应的补偿操作、。如果某步失败,就执行前面的补偿来"回滚"。

例如,在订票系统中:
- T1: 扣款(补偿:退款)
- T2: 预订座位(补偿:取消预订)
Saga 分为编排式:中央协调器和协同式:事件驱动。它牺牲了一些隔离性,但提高了可用性和性能,适合业务复杂场景。
TCC模式
TCC 全称为 Try-Confirm-Cancel,是分布式事务的一种补偿式模式。 它将一个分布式事务拆分成三个阶段:Try(尝试预留资源)、Confirm(确认提交变化)和 Cancel(取消回滚)。这种设计源于业务补偿的思想,适用于微服务环境,其中每个服务独立管理自己的数据,但需要协调整体一致性。
下面是一个典型的 TCC 流程图,展示了协调器与参与者的交互:

TCC 的本质是将事务分成可补偿的子操作,每个参与服务都需要实现 Try、Confirm 和 Cancel 接口。事务协调器(通常是一个中央服务或框架如 Seata、DTM)负责调度这些阶段。考虑下面的代码:
java
// Try: 冻结资金
accountA.tryDeduct(100); // A 账户冻结 100
accountB.tryDeduct(100); // B 账户预占 100(防透支)
// Confirm: 真正转账(所有 Try 成功才执行)
accountA.confirmDeduct(100);
accountB.confirmAdd(100);
// Cancel: 释放冻结(任一 Try 失败则执行)
accountA.cancelDeduct(100);
accountB.cancelDeduct(100);
1. Try 阶段:资源预留
这是 TCC 的第一步,参与者尝试执行业务操作,但不真正提交变化,而是预留资源。例如,在转账场景中:
- 服务 A(扣款方):检查余额,预扣金额(但不实际转出)。
- 服务 B(收款方):预增加金额(但不实际入账)。
Try 阶段必须是幂等的(可重复执行无副作用),以防网络重试。如果任何参与者失败,整个事务进入 Cancel。 这个阶段类似于"试探",锁定最小资源,避免长时间阻塞。
2. Confirm 阶段:确认提交
如果所有 Try 都成功,协调器发起 Confirm。参与者正式提交变化:
- 服务 A:实际扣除金额。
- 服务 B:实际增加金额。
Confirm 也需幂等,因为可能重试。这个阶段假设 Try 已成功,所以通常很快完成。
3. Cancel 阶段:补偿回滚
如果 Try 中有失败,或其他异常,协调器触发 Cancel。参与者执行补偿操作:
- 服务 A:恢复预扣金额。
- 服务 B:取消预增加。
Cancel 同样幂等。关键是补偿必须可靠,否则可能导致数据不一致。
TCC 的流程强调业务入侵:开发者需手动实现这些接口,但这也带来了灵活性,比如自定义锁定粒度。 另一个流程示意图,突出嵌套事务支持:
与 Saga相比:两者都基于补偿。Saga 是序列化子事务(每个有正向和补偿),更适合长事务链。TCC 则结构化(三个固定阶段),更适合资源预留场景。 Saga 可能正反操作不对称,TCC 的 Confirm/Cancel 更对称。两者都依赖本地事务,但 TCC 更注重预锁。
在 NoSQL 环境中,TCC 和 Saga 都很流行,因为它们不依赖传统 ACID。
三、常见的工程化实现
XA
XA 的实现依赖两阶段提交(2PC)协议。 它涉及两个角色:
- 事务管理器 ™:协调全局事务,通常由应用服务器或框架(如 Spring)实现。
- 资源管理器 (RM):实际执行操作的资源,如 MySQL 或 Oracle 数据库。
协议分为两个阶段:
- 准备阶段 (Prepare):TM 向所有 RM 发送 xa_prepare 请求。每个 RM 执行本地事务(e.g., 更新数据),但不提交,而是锁定资源并投票"Yes"(可提交)或"No"(失败)。
- 提交阶段 (Commit/Rollback):如果所有 RM 投票"Yes",TM 发送 xa_commit,大家提交;否则,发送 xa_rollback,大家回滚。
直接地说XA就是2PC的一个实现规范