分布式事务

分布式事务详解


目录

  1. 分布式事务背景
  2. 2PC(两阶段提交)
  3. 3PC(三阶段提交)
  4. TCC(Try-Confirm-Cancel)
  5. [Saga 模式](#Saga 模式)
  6. [AT 模式](#AT 模式)
  7. 可靠消息模式
  8. [阿里 Seata 分布式事务框架](#阿里 Seata 分布式事务框架)
  9. 总结对比

一、分布式事务背景

1.1 从单体到分布式

在传统的单体架构中,应用的所有模块运行在同一个进程中,共享同一个数据库实例。数据库事务(ACID)天然保证了一致性:

复制代码
┌────────────────────────────────┐
│         单体应用               │
│  ┌──────┐ ┌──────┐ ┌──────┐  │
│  │订单模块│ │库存模块│ │支付模块│  │
│  └──┬───┘ └──┬───┘ └──┬───┘  │
│     └────────┼────────┘       │
│         ┌────▼────┐           │
│         │ 单一数据库 │           │
│         └─────────┘           │
└────────────────────────────────┘

随着业务规模增长,单体架构暴露出大量问题:代码耦合严重、扩展困难、部署周期长、故障隔离性差。于是微服务/分布式架构应运而生:

复制代码
┌──────────┐  ┌──────────┐  ┌──────────┐
│ 订单服务  │  │ 库存服务  │  │ 支付服务  │
│  ┌──────┐ │  │  ┌──────┐ │  │  ┌──────┐ │
│  │订单DB │ │  │  │库存DB │ │  │  │支付DB │ │
│  └──────┘ │  │  └──────┘ │  │  └──────┘ │
└──────────┘  └──────────┘  └──────────┘

1.2 分布式事务的核心问题

拆分后,每个服务拥有独立的数据库,一个跨服务的业务操作(如"下单")需要同时操作多个数据库,无法再使用单机数据库事务来保证一致性。

典型场景:电商下单

复制代码
下单流程:
1. 订单服务 → 创建订单记录(订单数据库)
2. 库存服务 → 扣减商品库存(库存数据库)
3. 支付服务 → 扣减用户余额(支付数据库)

要求:三者要么全部成功,要么全部回滚

如果第 1、2 步成功,第 3 步失败,订单和库存已经变更,但支付未完成,数据就处于不一致状态。

1.3 核心理论基石

CAP 定理

由 Eric Brewer 在 2000 年提出,分布式系统无法同时满足以下三点,最多只能满足其中两点:

属性 含义 放弃的后果
C(Consistency)一致性 所有节点在同一时刻看到相同的数据 放弃 C:允许读取到旧数据(最终一致性)
A(Availability)可用性 每个请求都能获得非错误的响应 放弃 A:部分请求可能被阻塞或超时
P(Partition Tolerance)分区容错性 系统在出现网络分区时仍能正常工作 放弃 P:网络分区时系统停止服务(不现实)

关键结论: 在分布式系统中,网络分区是不可避免的,因此 P 必须满足。实际选择是 CP 或 AP:

  • CP 系统:发生分区时,牺牲可用性,保证一致性(如 2PC、ZooKeeper)
  • AP 系统:发生分区时,牺牲一致性,保证可用性(如 Saga、TCC 的最终一致性)
BASE 理论

BASE 是 CAP 定理中 AP 方案的实践指导,是对 ACID 的弱化:

属性 含义
BA(Basically Available)基本可用 系统出现故障时允许损失部分可用性,但核心功能仍然可用
S(Soft State)软状态 允许系统存在中间状态,该中间状态不影响系统整体可用性
E(Eventually Consistent)最终一致性 系统中的所有数据副本经过一段时间后,最终达到一致状态

ACID 与 BASE 对比如下:

维度 ACID BASE
一致性 强一致性 最终一致性
隔离性 强隔离 弱隔离
可用性 低 高
适用场景 金融、银行等强一致性需求 互联网电商、社交等高并发场景

二、2PC(两阶段提交)

2.1 概述

2PC(Two-Phase Commit,两阶段提交)是最经典的分布式事务协议,由协调者(Coordinator)和多个参与者(Cohort/Participant)组成。顾名思义,它把事务提交过程分为两个阶段:准备阶段 和提交阶段。

2.2 角色定义

角色 职责
协调者(Coordinator) 事务管理器,负责发起、协调和决策事务的提交或回滚
参与者(Participant) 资源管理器(如数据库),负责执行事务的本地操作

2.3 流程详解

阶段一:准备阶段(Prepare Phase / Voting Phase)
复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 Prepare 请求 ────────►│
  │                                   │ ② 执行本地事务(写 undo/redo 日志)
  │                                   │    但不提交
  │                                   │ ③ 锁定相关资源
  │◄─── ④ 回复 YES(可提交)or NO(失败)──│
  │                                   │

协调者做的事情:

  1. 向所有参与者发送 Prepare 消息
  2. 等待所有参与者的响应

参与者做的事情:

  1. 收到 Prepare 请求后,执行本地事务操作,记录 undo(回滚日志)和 redo(重做日志)
  2. 事务未提交,资源处于锁定状态
  3. 回复协调者:如果本地事务执行成功,回复 "YES"(可以提交);如果执行失败,回复 "NO"(需要回滚)
阶段二:提交阶段(Commit Phase)

情况 A:所有参与者都回复 YES(提交)

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 Commit 请求 ────────►│
  │                                   │ ② 提交本地事务
  │                                   │ ③ 释放锁资源
  │◄─── ④ 回复 ACK(确认)────────│
  │                                   │

情况 B:任一参与者回复 NO 或超时(回滚)

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 Rollback 请求 ──────►│
  │                                   │ ② 使用 undo 日志回滚
  │                                   │ ③ 释放锁资源
  │◄─── ④ 回复 ACK(确认)────────│
  │                                   │

2.4 优缺点

优点:

优点 说明
原理简单 只需要两个阶段,逻辑清晰,易于理解和实现
强一致性 保证所有参与者要么全部提交,要么全部回滚(原子性)
行业标准 有 XA 规范支撑,多数数据库(MySQL、Oracle、PostgreSQL)都支持 XA 事务

缺点:

缺点 详细说明
同步阻塞 参与者在准备阶段锁定资源后,必须等待协调者指令才能继续,期间资源被锁定,其他事务无法访问,并发性能极差
单点故障 协调者是整个系统的单点,一旦协调者宕机,所有参与者将无限期阻塞,整个系统瘫痪
数据不一致风险 提交阶段中,如果协调者只向部分参与者发送了 Commit 指令后宕机,部分参与者提交了、部分未提交,数据出现不一致
网络开销大 每个事务需要多次网络往返,延迟较高
不适合高并发 因为资源锁定时间长,不适合高并发互联网场景
容错性差 对网络分区和节点故障的容忍能力有限

2.5 当前主流采用情况

2PC/XA 在互联网主流场景中已基本被淘汰,原因如下:

  1. 互联网场景追求高并发、低延迟,2PC 的资源锁定和同步阻塞完全不可接受
  2. 微服务架构下,服务之间通过 REST/RPC 通信,而非直接使用数据库 XA 协议
  3. 长事务场景下锁竞争会导致系统雪崩

仍有小范围使用的场景:

  • 传统企业应用(银行核心系统、ERP 等强一致性需求)
  • 跨数据库操作(同一应用内跨多个数据库实例)
  • 金融机构的某些合规场景

三、3PC(三阶段提交)

3.1 概述

3PC(Three-Phase Commit,三阶段提交)是 2PC 的改进版本,在 2PC 的两个阶段之间插入了一个预提交阶段(Pre-Commit) ,旨在解决 2PC 的同步阻塞和单点故障问题。同时引入了超时机制,允许参与者在协调者长时间无响应时自行决策。

3.2 流程详解

阶段一:CanCommit(询问阶段)
复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 CanCommit 请求 ──────►│
  │                                   │ ② 判断是否可以执行事务
  │                                   │    (不执行事务,仅做可行性判断)
  │◄─── ③ 回复 YES 或 NO ──────────│
  │                                   │

协调者: 向所有参与者发送 CanCommit 请求,询问是否可以执行事务

参与者: 根据自身状态判断能否执行事务(如检查网络状况、资源可用性等),不执行任何事务操作,回复 YES 或 NO

异常处理: 如果任何参与者回复 NO 或超时,协调者直接终止事务

阶段二:PreCommit(预提交阶段)

情况 A:所有参与者回复 YES

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 PreCommit 请求 ──────►│
  │                                   │ ② 执行本地事务(写 undo/redo 日志)
  │                                   │    但不提交
  │                                   │ ③ 锁定相关资源
  │◄─── ④ 回复 ACK 或 NO ──────────│
  │                                   │

情况 B:有参与者回复 NO 或超时

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 Abort(中断)请求 ───►│
  │                                   │ ② 回滚(或无需操作)
  │◄─── ③ 回复 ACK ────────────────│
  │                                   │
阶段三:DoCommit(提交阶段)

情况 A:协调者收到所有 PreCommit 的 ACK

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 DoCommit 请求 ──────►│
  │                                   │ ② 提交本地事务
  │                                   │ ③ 释放锁资源
  │◄─── ④ 回复 ACK ────────────────│
  │                                   │

情况 B:协调者未收到所有 ACK 或超时

复制代码
协调者                              参与者
  │                                   │
  │──── ① 发送 Rollback 请求 ──────►│
  │                                   │ ② 使用 undo 日志回滚
  │                                   │ ③ 释放锁资源
  │◄─── ④ 回复 ACK ────────────────│
  │                                   │

3.3 核心改进:参与者的超时中断机制

3PC 相比 2PC 最大的改进是:在 DoCommit 阶段,如果参与者长时间没有收到协调者的指令,它不会无限期阻塞,而是会自动提交事务。

复制代码
参与者超时后的决策逻辑:

在 PreCommit 阶段超时:
  → 参与者不确定协调者是否存活,也不确定其他参与者的状态
  → 安全策略:回滚事务(因为可能还没进入 DoCommit 阶段)

在 DoCommit 阶段超时:
  → 参与者已经收到了 PreCommit 指令,说明协调者已经决定提交
  → 安全策略:自动提交事务(因为其他参与者大概率也收到了 PreCommit)

这个机制解决了 2PC 中协调者宕机后参与者无限阻塞的问题。

3.4 优缺点

优点:

优点 说明
减少阻塞 引入了超时机制,参与者在 DoCommit 阶段超时后可自动提交,不会无限期阻塞
增加容错性 协调者故障后,参与者可以在超时后自行决策,降低了单点故障的影响
PreCommit 缓冲 在准备阶段前增加了 CanCommit 询问阶段,可以提前发现不可用的参与者,减少不必要的资源锁定

缺点:

缺点 详细说明
实现复杂 比 2PC 多了一个阶段,增加了代码复杂度和调试难度
网络开销更大 三个阶段需要至少三次网络往返,延迟比 2PC 更高
数据不一致风险仍存在 在网络分区场景下,如果部分参与者收到 PreCommit 后与协调者失联,它们超时后自动提交;但协调者可能因为未收到 ACK 而发送回滚指令,导致部分提交、部分回滚的不一致
性能差 相比 2PC,多了一个阶段,延迟更高;资源锁定时间仍然较长
假设不可靠 "进入 DoCommit 阶段就自动提交"的假设在网络分区场景下可能不成立,协调者可能决定回滚

3.5 当前主流采用情况

3PC 在实际生产中极少被采用,原因如下:

  1. 虽然理论上解决了 2PC 的阻塞问题,但引入了新的数据不一致风险(网络分区时自动提交可能导致数据不一致)
  2. 增加了网络开销,性能更差
  3. 实现复杂度高,而收益有限
  4. 在互联网场景中,人们更倾向于使用最终一致性的方案(如 Saga、TCC)

3PC 更多是学术和研究价值,作为分布式一致性协议的理论演进路径的一部分,后来被 Paxos/Raft 等共识算法所取代。


四、TCC(Try-Confirm-Cancel)

4.1 概述

TCC(Try-Confirm-Cancel)是一种补偿型 的分布式事务方案,由 Pat Helland 在 2007 年提出。TCC 的核心思想是:将每个服务的操作拆分为三个阶段,通过预留资源 + 确认 / 取消来实现最终一致性。

TCC 属于最终一致性方案,不要求资源锁定,而是通过"预留"和"补偿"来保证数据最终一致。

4.2 三个阶段

阶段一:Try(尝试/预留)
复制代码
Try 阶段:对所有服务进行资源检查和预留

- 订单服务:将订单状态设置为"预创建",冻结库存
- 库存服务:检查库存是否充足,扣减冻结库存(但不减少实际可售库存)
- 支付服务:检查余额是否充足,冻结对应金额

Try 阶段的核心要求:

  1. 完成所有业务检查(一致性检查)
  2. 预留必须的业务资源,但不实际执行核心操作
  3. 预留的资源应保证后续 Confirm 阶段能够成功执行
阶段二:Confirm(确认)
复制代码
Confirm 阶段:所有 Try 成功后,执行真正的业务操作

- 订单服务:将订单状态从"预创建"改为"已创建"
- 库存服务:从冻结库存中扣除,更新实际库存
- 支付服务:从冻结金额中扣除,更新实际余额

Confirm 阶段的核心要求:

  1. 必须幂等,允许重试
  2. 如果没有异常,Confirm 一定会成功(因为 Try 阶段已经预留了资源)
  3. 如果 Confirm 失败,需要不断重试直到成功
阶段三:Cancel(取消/补偿)
复制代码
Cancel 阶段:任一 Try 失败时,对所有已 Try 成功的服务进行补偿

- 订单服务:将订单状态从"预创建"改为"已取消"
- 库存服务:释放冻结库存,恢复可售库存
- 支付服务:释放冻结金额,恢复可用余额

Cancel 阶段的核心要求:

  1. 必须幂等,允许重试
  2. 释放 Try 阶段预留的资源
  3. 如果 Cancel 失败,需要不断重试直到成功

4.3 流程示例

以电商下单为例,展示 TCC 的完整流程:

复制代码
场景:用户下单购买商品,价格为 100 元

┌─────────────────────────────────────────────────────┐
│                    TCC 协调者                         │
└──┬──────────────────┬──────────────────┬────────────┘
   │                  │                  │
   ▼                  ▼                  ▼
┌──────┐          ┌──────┐          ┌──────┐
│订单服务│          │库存服务│          │支付服务│
└──────┘          └──────┘          └──────┘

===== Try 阶段 =====
订单服务: 创建订单(状态=预创建),冻结商品
库存服务: 检查库存>=1,冻结库存 -1
支付服务: 检查余额>=100,冻结余额 100

===== 全部 Try 成功 → Confirm 阶段 =====
订单服务: 订单状态:预创建 → 已创建
库存服务: 冻结库存扣减生效,实际库存 -1
支付服务: 冻结余额扣减生效,实际余额 -100

===== 任一步 Try 失败 → Cancel 阶段 =====
假设:支付服务 Try 失败(余额不足)
订单服务: 取消订单(状态=预创建 → 已取消),释放冻结
库存服务: 释放冻结库存,冻结库存 +1
支付服务: 无需操作(Try 阶段未成功)

4.4 补偿机制(重点)

TCC 的补偿机制是其核心所在,与 2PC 的 undo 日志回滚不同,TCC 的补偿是业务层面的,需要开发者自己实现。

补偿策略
复制代码
┌────────────────────────────────────────────────┐
│               TCC 补偿机制                       │
├────────────────────────────────────────────────┤
│                                                 │
│  1. 空回滚(Null Rollback)                     │
│     - 场景:Try 请求未到达服务或超时,协调者     │
│       直接发送了 Cancel 指令                    │
│     - 处理:Cancel 接口需要识别到没有对应的 Try  │
│       记录,直接返回成功(空回滚)               │
│     - 实现:通常在数据库记录 Try 操作日志,      │
│       Cancel 时检查日志是否存在                  │
│                                                 │
│  2. 防悬挂(Prevent Suspension)                │
│     - 场景:Cancel 先于 Try 到达(网络乱序),   │
│       之后 Try 再到达                           │
│     - 处理:Try 接口需要检查是否有对应的 Cancel  │
│       记录,如果有则拒绝执行 Try                 │
│     - 实现:Cancel 时记录操作日志,Try 时检查    │
│                                                 │
│  3. 幂等性(Idempotence)                       │
│     - Try/Confirm/Cancel 都可能被重试,必须保证  │
│       多次调用结果一致                           │
│     - 实现:基于唯一事务 ID 做去重判断           │
│                                                 │
│  4. 重试机制(Retry)                           │
│     - Confirm/Cancel 失败后需要不断重试          │
│     - 重试策略:指数退避、固定间隔等             │
│     - 人工兜底:超过最大重试次数后转为人工处理   │
│                                                 │
└────────────────────────────────────────────────┘
补偿代码示例
java 复制代码
// 库存服务的 TCC 实现

@Service
public class InventoryTccService {

    // Try:预留库存
    @Transactional
    public boolean tryDeductStock(String txId, String productId, int count) {
        // 防悬挂:检查是否已有 Cancel 记录
        if (tccLogDao.existsCancel(txId)) {
            return false; // 拒绝执行 Try
        }
        // 幂等:检查是否已经 Try 过
        if (tccLogDao.existsTry(txId)) {
            return true; // 已 Try 过,直接返回成功
        }
        // 业务检查
        int frozenStock = productDao.getFrozenStock(productId);
        int availableStock = productDao.getAvailableStock(productId);
        if (availableStock < count) {
            return false; // 库存不足
        }
        // 减少可用库存,增加冻结库存
        productDao.decreaseAvailableStock(productId, count);
        productDao.increaseFrozenStock(productId, count);
        // 记录 Try 日志
        tccLogDao.insertTryLog(txId, productId, count);
        return true;
    }

    // Confirm:确认扣减
    @Transactional
    public boolean confirmDeductStock(String txId, String productId, int count) {
        // 幂等检查
        if (tccLogDao.existsConfirm(txId)) {
            return true;
        }
        // 减少冻结库存,更新总库存
        productDao.decreaseFrozenStock(productId, count);
        productDao.decreaseTotalStock(productId, count);
        // 记录 Confirm 日志
        tccLogDao.insertConfirmLog(txId, productId, count);
        return true;
    }

    // Cancel:释放冻结库存
    @Transactional
    public boolean cancelDeductStock(String txId, String productId, int count) {
        // 空回滚:如果 Try 日志不存在,说明 Try 未执行
        if (!tccLogDao.existsTry(txId)) {
            // 记录 Cancel 日志(防悬挂用)
            tccLogDao.insertCancelLog(txId, productId, count);
            return true; // 空回滚,直接返回成功
        }
        // 幂等检查
        if (tccLogDao.existsCancel(txId)) {
            return true;
        }
        // 恢复可用库存,减少冻结库存
        productDao.increaseAvailableStock(productId, count);
        productDao.decreaseFrozenStock(productId, count);
        // 记录 Cancel 日志
        tccLogDao.insertCancelLog(txId, productId, count);
        return true;
    }
}

4.5 优缺点

优点:

优点 说明
无资源锁定 不像 2PC 那样锁定数据库资源,Try 阶段只做"预留",不阻塞其他事务,并发性能高
最终一致性 通过 Confirm/Cancel 保证数据最终一致,适合高并发互联网场景
业务可控 由业务方自己实现 Try/Confirm/Cancel,可以灵活处理各种业务场景
性能好 没有全局锁,各服务可以并行执行,不依赖单一协调者进行强一致性协调
容错性好 通过重试机制保证 Confirm/Cancel 最终成功,单点故障影响小

缺点:

缺点 详细说明
侵入性强 每个参与的服务都需要实现 Try/Confirm/Cancel 三个接口,对业务代码侵入大
开发成本高 需要处理空回滚、防悬挂、幂等性等复杂问题,开发工作量大
实现难度大 业务逻辑的补偿操作往往比正向操作更复杂,容易出错
维护成本高 业务逻辑变更时,需要同时修改 Try/Confirm/Cancel 三个接口
不适合长事务 如果 Confirm 阶段耗时过长,整个事务时间会拉长,但好在没有锁
数据一致性弱 在 Try 和 Confirm 之间,数据处于中间状态(冻结状态),外部可能读到不一致数据

4.6 当前主流采用情况

TCC 是目前互联网公司中较为主流的分布式事务方案,尤其适用于以下场景:

  1. 金融/支付场景:对资金安全要求高,需要精确的补偿机制
  2. 电商核心交易链路:下单、扣库存、扣款等需要强保证的场景
  3. 跨服务数据一致性场景:微服务架构下需要保证最终一致性的场景

代表实践:

  • 蚂蚁集团:大量使用 TCC 处理支付、转账等核心交易场景
  • 美团、滴滴等:在订单、支付等核心流程中使用 TCC
  • 阿里的 Seata 框架内置了 TCC 模式支持

五、Saga 模式

5.1 概述

Saga 模式最早由 Hector Garcia-Molina 在 1987 年提出,用于解决**长事务(Long-Lived Transaction)**问题。Saga 的核心思想是:将一个长事务拆分为多个本地事务,每个本地事务都有对应的补偿事务,当某个本地事务失败时,按相反顺序执行之前成功事务的补偿操作。

复制代码
Saga 事务 = 一系列本地事务 + 对应的补偿事务

T1 → T2 → T3 → ... → Tn
C1   C2   C3         Cn

如果 T3 失败:
  → 执行 C2(补偿 T2)→ 执行 C1(补偿 T1)

5.2 两种协调模式

模式一:编排式(Choreography)
复制代码
┌────────┐  事件A  ┌────────┐  事件B  ┌────────┐
│ 服务A  │────────►│ 服务B  │────────►│ 服务C  │
│ (订单) │◄────────│ (库存) │◄────────│ (支付) │
└────────┘  补偿事件  └────────┘  补偿事件  └────────┘

特点:

  • 每个服务独立监听事件并执行自己的事务
  • 自己决定何时执行本地事务,并发布事件触发下一个服务
  • 失败时发布补偿事件,由上游服务自己处理补偿

优点: 松耦合,服务之间不直接依赖

缺点: 流程分散在各个服务中,难以追踪和理解整体流程

模式二:编排器式(Orchestration)
复制代码
                    ┌──────────────┐
                    │  Saga 编排器   │
                    │ (Order Saga) │
                    └──┬───┬───┬──┘
                       │   │   │
                  ┌────▼┐ ┌▼───┐ ┌────▼┐
                  │订单服务│ │库存服务│ │支付服务│
                  └──────┘ └─────┘ └──────┘

特点:

  • 由一个 Saga 编排器(Orchestrator)集中管理整个流程
  • 编排器按顺序调用各个服务,并处理失败补偿
  • 流程逻辑集中,便于管理和追踪

优点: 流程集中管理,清晰可控,易于维护

缺点: 编排器成为关键节点,逻辑复杂度集中

目前业界以编排器式为主流,因为它更易于管理和维护。

5.3 流程示例

以电商下单为例,展示 Saga 编排器模式的完整流程:

复制代码
Saga 编排器:OrderSaga

步骤 1:创建订单
  → 调用订单服务.createOrder()
  → 成功:记录补偿函数 = 订单服务.cancelOrder(orderId)
  → 失败:终止 Saga,无需补偿

步骤 2:扣减库存
  → 调用库存服务.deductStock()
  → 成功:记录补偿函数 = 库存服务.restoreStock(productId, count)
  → 失败:执行补偿步骤 1(取消订单)

步骤 3:扣减余额
  → 调用支付服务.deductBalance()
  → 成功:Saga 完成
  → 失败:执行补偿步骤 2(恢复库存)→ 执行补偿步骤 1(取消订单)

5.4 补偿机制(重点)

Saga 的补偿机制是反向补偿 ,即按照事务执行的逆序执行补偿操作。

补偿的核心原则
复制代码
Saga 补偿的三大原则:

1. 逆序补偿(Backward Recovery)
   - 从失败点开始,向前(逆序)依次执行补偿
   - 例如:T1→T2→T3(失败),补偿顺序:C2→C1

2. 补偿幂等
   - 补偿操作可能被重试,必须保证幂等性
   - 同一个补偿操作多次执行,结果一致

3. 补偿必须成功
   - 补偿操作失败后需要不断重试,直到成功
   - 超过重试上限需要人工介入
补偿策略详解
复制代码
┌──────────────────────────────────────────────────────┐
│                  Saga 补偿策略                         │
├──────────────────────────────────────────────────────┤
│                                                       │
│  1. 正向补偿(Forward Recovery)                      │
│     - 适用场景:失败后可以重试当前步骤(非致命错误)    │
│     - 处理方式:重试失败的步骤,不触发逆序补偿         │
│     - 示例:库存扣减因网络超时失败,重试可能成功       │
│                                                       │
│  2. 反向补偿(Backward Recovery)                     │
│     - 适用场景:当前步骤是业务失败,无法重试           │
│     - 处理方式:逆序执行所有成功步骤的补偿             │
│     - 示例:余额不足,无法完成支付,需要回滚整个订单   │
│                                                       │
│  3. 混合补偿(Mixed Recovery)                        │
│     - 适用场景:部分步骤可重试,部分不可重试           │
│     - 处理方式:先重试可重试的步骤,超时后转为反向补偿  │
│                                                       │
│  4. 隔离性处理(Isolation)                           │
│     - 问题:Saga 是多个独立的本地事务,中间状态        │
│       可能被其他事务读取,导致脏读                     │
│     - 解决方案:                                       │
│       a. 语义锁:在业务字段上标记"处理中"状态          │
│       b. 版本号/乐观锁:更新时检查版本号               │
│       c. 事务间写隔离:确保冲突操作有合理的顺序         │
│                                                       │
└──────────────────────────────────────────────────────┘
补偿代码示例
java 复制代码
// Saga 编排器实现

@Component
public class OrderSagaOrchestrator {

    @Autowired
    private OrderService orderService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PaymentService paymentService;

    @Transactional
    public void executeOrderSaga(OrderRequest request) {
        // 记录 Saga 执行日志
        SagaLog sagaLog = new SagaLog(request.getOrderId());
        sagaLogDao.insert(sagaLog);

        try {
            // 步骤 1:创建订单
            Order order = orderService.createOrder(request);
            sagaLog.addStep("CREATE_ORDER", () -> {
                orderService.cancelOrder(order.getId()); // 补偿函数
            });

            // 步骤 2:扣减库存
            inventoryService.deductStock(request.getProductId(), request.getCount());
            sagaLog.addStep("DEDUCT_STOCK", () -> {
                inventoryService.restoreStock(request.getProductId(), request.getCount());
            });

            // 步骤 3:扣减余额
            paymentService.deductBalance(request.getUserId(), request.getAmount());
            sagaLog.addStep("DEDUCT_BALANCE", () -> {
                paymentService.refundBalance(request.getUserId(), request.getAmount());
            });

            // 全部成功
            sagaLog.setStatus(SagaStatus.COMPLETED);
            sagaLogDao.update(sagaLog);

        } catch (Exception e) {
            // 执行补偿:逆序执行所有已记录的补偿函数
            sagaLog.compensate(); // 从最后一步开始向前补偿
            sagaLog.setStatus(SagaStatus.COMPENSATED);
            sagaLogDao.update(sagaLog);
            throw new SagaCompensationException("Saga 补偿完成", e);
        }
    }
}

// SagaLog 补偿逻辑
public class SagaLog {
    private List<SagaStep> steps = new ArrayList<>();

    public void compensate() {
        // 逆序补偿
        for (int i = steps.size() - 1; i >= 0; i--) {
            SagaStep step = steps.get(i);
            try {
                // 检查是否已补偿(幂等)
                if (!step.isCompensated()) {
                    step.getCompensation().execute(); // 执行补偿
                    step.setCompensated(true);
                }
            } catch (Exception e) {
                // 补偿失败,记录日志,异步重试或人工处理
                compensationLogDao.insert(new CompensationLog(step, e));
                // 继续执行前一个步骤的补偿(尽力补偿)
            }
        }
    }
}

5.5 优缺点

优点:

优点 说明
高可用 没有全局锁,各服务独立执行,不阻塞其他事务,可用性极高
高性能 本地事务执行快,不需要等待其他参与者,并发性能好
适合长事务 不锁定资源,事务可以持续很长时间,适合工作流、审批流等场景
松耦合 编排式模式下,服务之间通过事件异步通信,耦合度低
容错性好 通过补偿机制处理失败,没有单点故障问题

缺点:

缺点 详细说明
缺乏隔离性 Saga 的各步骤是独立的本地事务,中间状态可见,可能导致脏读、不可重复读等问题
补偿实现复杂 需要为每个正向操作编写对应的补偿操作,业务逻辑逆向还原往往很困难
补偿可能失败 补偿操作本身可能失败(如服务宕机),需要重试和人工兜底机制
无自动回滚 不像 2PC 那样数据库自动回滚,需要手动编写所有补偿逻辑
数据一致性弱 只有在所有 Saga 步骤完成后,数据才最终一致,中间可能存在不一致窗口

5.6 与 TCC 的对比

维度 TCC Saga
资源预留 有(Try 阶段预留资源) 无(直接执行)
事务失败处理 Cancel 回滚 逆序补偿
隔离性 较好(Try 预留资源) 较差(执行即生效)
实现复杂度 高(3 个接口) 中(1 个正向 + 1 个补偿)
代码侵入性 高 中
适用场景 金融、支付等强一致性需求 长事务、工作流、非核心业务

5.7 当前主流采用情况

Saga 模式是当前微服务架构中最主流的分布式事务方案之一,原因如下:

  1. 互联网追求高可用、高并发,Saga 的无锁特性完美契合
  2. 微服务架构天然适合 Saga 的编排式或编排器式模式
  3. 补偿逻辑虽复杂,但比 TCC 侵入性小,更易于实现
  4. 适合长事务场景,这是其他方案难以解决的

代表实践:

  • Netflix:Conductor 工作流引擎使用 Saga 处理视频编码等长事务
  • Uber:使用 Saga 处理打车订单的生命周期
  • 阿里的 Seata 框架内置 Saga 模式支持
  • 各类微服务框架(如 Spring Cloud、Axon Framework)都提供了 Saga 支持

六、AT 模式

6.1 概述

AT 模式(Automatic Transaction,自动事务)是阿里 Seata 框架独创的分布式事务模式,它是对 2PC 的改进和优化 。AT 模式的核心思想是:基于关系型数据库的本地事务,通过自动生成 undo 日志(前镜像和后镜像)实现自动回滚,对业务代码零侵入。

AT 模式本质上是一种改良版的 2PC,它解决了 2PC 的以下问题:

  • 不需要 XA 协议支持,基于普通本地事务
  • 锁粒度更细(全局锁而不是数据库锁)
  • 只在第一阶段短暂的锁定资源

6.2 架构角色

复制代码
┌──────────────────────────────────────────────────────┐
│                     Seata 架构                        │
├──────────────────────────────────────────────────────┤
│                                                       │
│  ┌──────────┐      ┌──────────┐                      │
│  │ TC(事务   │      │ TM(事务  │                      │
│  │ 协调器)   │◄────►│ 管理器)  │                      │
│  └────┬─────┘      └────┬─────┘                      │
│       │                 │                             │
│       │        ┌────────┴────────┐                    │
│       │        ▼                 ▼                    │
│       │  ┌──────────┐      ┌──────────┐              │
│       └──►│ RM(资源  │      │ RM(资源  │              │
│           │ 管理器)  │      │ 管理器)  │              │
│           └──────────┘      └──────────┘              │
│                                                       │
└──────────────────────────────────────────────────────┘
角色 英文 说明
TC(事务协调器) Transaction Coordinator 独立部署的服务,负责维护全局事务和分支事务的状态,驱动全局提交或回滚
TM(事务管理器) Transaction Manager 定义全局事务的范围:开始全局事务、提交或回滚全局事务
RM(资源管理器) Resource Manager 管理分支事务处理的资源,与 TC 通信以注册分支事务和报告分支事务状态,并驱动分支事务提交或回滚

6.3 流程详解

两阶段流程
复制代码
第一阶段:执行 + 注册

┌──────────────────────────────────────┐
│ TM:开启全局事务                       │
│   → 向 TC 注册全局事务,获取 XID       │
│   → 将 XID 传播到各个微服务(通过 RPC) │
└──────────────────────────────────────┘
                    │
    ┌───────────────┼───────────────┐
    ▼               ▼               ▼
┌──────┐       ┌──────┐       ┌──────┐
│  RM1  │       │  RM2  │       │  RM3  │
│ 执行SQL│       │ 执行SQL│       │ 执行SQL│
│ 保存前镜像│      │ 保存前镜像│      │ 保存前镜像│
│ 执行SQL│       │ 执行SQL│       │ 执行SQL│
│ 保存后镜像│      │ 保存后镜像│      │ 保存后镜像│
│ 生成undo│       │ 生成undo│       │ 生成undo│
│ 提交本地事务│     │ 提交本地事务│     │ 提交本地事务│
│ 注册分支事务│     │ 注册分支事务│     │ 注册分支事务│
│ 释放本地锁│      │ 释放本地锁│      │ 释放本地锁│
│ 获取全局锁│      │ 获取全局锁│      │ 获取全局锁│
└──────┘       └──────┘       └──────┘
    │               │               │
    └───────────────┼───────────────┘
                    ▼
┌──────────────────────────────────────┐
│ TC:等待所有分支事务注册完成           │
│   → 收到所有注册后,向 TM 报告完成     │
└──────────────────────────────────────┘


第二阶段:全局提交

TC 根据所有分支事务的报告决定全局提交或回滚:

┌──── 全局提交 ────┐     ┌──── 全局回滚 ────┐
│                   │     │                   │
│ TC → RM: 异步删除  │     │ TC → RM: 异步回滚  │
│   undo 日志       │     │   使用 undo 日志   │
│                   │     │   回复到前镜像状态  │
│ RM: 释放全局锁     │     │ RM: 释放全局锁     │
│                   │     │                   │
└───────────────────┘     └───────────────────┘
前镜像与后镜像

AT 模式的核心是自动生成 undo 日志,通过解析 SQL 语句,自动记录数据变更前后的快照:

复制代码
// 执行 SQL 前,记录前镜像(Before Image)
SELECT id, name, balance FROM account WHERE id = 1;
// 前镜像: {id: 1, name: "张三", balance: 1000}

// 执行 UPDATE
UPDATE account SET balance = balance - 100 WHERE id = 1;

// 执行 SQL 后,记录后镜像(After Image)
SELECT id, name, balance FROM account WHERE id = 1;
// 后镜像: {id: 1, name: "张三", balance: 900}

// 生成的 undo 日志包含:
// 前镜像:{id: 1, name: "张三", balance: 1000}
// 后镜像:{id: 1, name: "张三", balance: 900}
// 回滚时,根据前镜像生成反向 SQL:
// UPDATE account SET balance = 1000 WHERE id = 1

6.4 全局锁与写隔离

AT 模式引入了全局锁的概念来解决并发写冲突:

复制代码
┌──────────────────────────────────────────────────────┐
│                  AT 模式的写隔离                       │
├──────────────────────────────────────────────────────┤
│                                                       │
│  事务A(持有全局锁)                                    │
│  ┌─────────────────────────────────────────┐          │
│  │ 1. 获取全局锁 ✓                          │          │
│  │ 2. 执行 UPDATE,提交本地事务              │          │
│  │ 3. 注册分支事务                          │          │
│  │ 4. 等待全局事务提交/回滚...               │          │
│  │ 5. 全局事务完成后,释放全局锁             │          │
│  └─────────────────────────────────────────┘          │
│                                                       │
│  事务B(等待全局锁)                                    │
│  ┌─────────────────────────────────────────┐          │
│  │ 1. 尝试获取全局锁 ✗(被事务A持有)        │          │
│  │ 2. 等待全局锁释放...                      │          │
│  │ 3. 全局锁释放后,重新获取                 │          │
│  │ 4. 继续执行                              │          │
│  └─────────────────────────────────────────┘          │
│                                                       │
└──────────────────────────────────────────────────────┘

写隔离机制: 在本地事务提交前,AT 模式会尝试获取该记录的全局锁。如果获取失败,则会等待或重试,直到超时。

6.5 补偿机制(重点)

AT 模式的补偿机制是其核心创新,相比于 2PC 和 TCC,它更加自动化:

自动回滚
复制代码
AT 模式回滚流程:

1. TC 收到 TM 的全局回滚请求
2. TC 向所有已注册的 RM 发送分支回滚请求
3. RM 收到回滚请求后:
   a. 通过 XID 和 Branch ID 找到对应的 undo 日志
   b. 验证当前数据是否与后镜像一致(数据脏写校验)
   c. 如果不一致,说明数据被其他事务修改,需要特殊处理
   d. 如果一致,根据前镜像生成反向 SQL 执行回滚
   e. 删除 undo 日志
   f. 提交本地事务
4. 向 TC 报告回滚完成
数据脏写校验
复制代码
数据校验逻辑:

回滚时,RM 会执行:
SELECT * FROM table WHERE id = ? FOR UPDATE  -- 获取当前行数据

比较当前数据与 undo 日志中的后镜像:

情况 1:当前数据 == 后镜像
  → 数据未被其他事务修改,可以安全回滚
  → 执行反向 SQL:UPDATE ... SET balance = 1000 WHERE id = 1

情况 2:当前数据 != 后镜像
  → 数据被其他事务修改(脏写)
  → 无法自动回滚,需要人工处理
  → 策略:记录异常日志,发送告警,人工介入
补偿机制总结
复制代码
┌──────────────────────────────────────────────────┐
│           AT 模式补偿机制总结                      │
├──────────────────────────────────────────────────┤
│                                                   │
│  1. 自动生成 undo 日志                            │
│     - 无需开发者编写补偿逻辑                       │
│     - 通过解析 SQL 自动生成前镜像和后镜像          │
│                                                   │
│  2. 自动回滚                                      │
│     - 全局回滚时,TC 自动通知所有 RM 执行回滚      │
│     - RM 根据 undo 日志自动生成反向 SQL            │
│                                                   │
│  3. 数据脏写校验                                  │
│     - 回滚前校验当前数据是否与后镜像一致            │
│     - 不一致时拒绝自动回滚,转人工处理              │
│                                                   │
│  4. 全局锁防止脏写                                │
│     - 在本地事务提交前获取全局锁                   │
│     - 全局事务完成前,其他事务无法修改同一行数据    │
│                                                   │
│  5. 补偿的幂等性                                  │
│     - undo 日志根据 XID + Branch ID 唯一标识      │
│     - 重复回滚请求不会产生副作用                   │
│                                                   │
│  6. 异步补偿                                      │
│     - 第二阶段是异步的,不阻塞业务                 │
│     - 提交/回滚失败会重试,直到成功或超时转为人工  │
│                                                   │
└──────────────────────────────────────────────────┘

6.6 优缺点

优点:

优点 说明
零侵入 对业务代码完全无侵入,只需添加一个注解 @GlobalTransactional,加上配置即可使用
自动补偿 无需开发者编写补偿逻辑,框架自动解析 SQL 生成 undo 日志和反向 SQL
高性能 第一阶段提交本地事务后释放本地锁,只持有全局锁,锁粒度更细,并发性能更好
行业标准兼容 基于普通本地事务(JDBC 事务),不依赖 XA 协议,兼容所有主流数据库
易于集成 与 Spring Boot、Spring Cloud、Dubbo 等框架深度集成,开箱即用

缺点:

缺点 详细说明
仅支持关系型数据库 依赖数据库的 ACID 事务和 SQL 解析,不支持 NoSQL、消息队列等
SQL 解析限制 仅支持 INSERT、UPDATE、DELETE 操作,不支持复杂 SQL(如存储过程、多表关联更新)
全局锁开销 第二阶段提交前需要持有全局锁,高并发时可能成为瓶颈
数据一致性风险 第一阶段提交后到第二阶段提交前,数据对外可见(本地事务已提交),存在中间状态
脏写风险 虽然有全局锁,但在极端情况下(如全局锁超时),仍可能出现脏写
依赖 Seata Server 需要独立部署 Seata Server(TC),增加了运维复杂度
数据膨胀 undo_log 表会随着事务量增长而膨胀,需要定期清理

6.7 当前主流采用情况

AT 模式在国内互联网公司中非常流行,尤其是在阿里系生态中广泛使用:

  1. 阿里系:淘宝、天猫、菜鸟等核心业务系统大量使用 AT 模式
  2. 国内互联网公司:美团、滴滴、字节跳动等公司都有使用 Seata AT 模式
  3. 中小型企业:因为 AT 模式零侵入、易用的特性,在中小型企业中非常受欢迎

AT 模式是当前国内分布式事务的事实标准之一,但国际上更倾向于使用 Saga 模式。


七、可靠消息模式

7.1 概述

可靠消息模式(Reliable Message Pattern)是一种基于消息队列实现最终一致性 的分布式事务方案。其核心思想是:通过消息中间件异步传递事务指令,上游服务先完成本地事务,再通过可靠的机制将消息投递给下游服务,保证上下游数据最终一致。

可靠消息模式不要求两阶段提交和回滚,而是通过"正向确认 + 重试"保证消息一定被消费,下游服务消费消息时通过幂等设计保证数据正确。

复制代码
┌──────────┐  ① 发送半消息    ┌──────────┐
│  上游服务  │ ───────────────► │ 消息中间件  │
│  (订单)   │◄─────────────── │ (RocketMQ) │
└────┬─────┘  ② 半消息确认     └────┬─────┘
     │                              │
     │ ③ 执行本地事务               │ ④ 提交/回滚消息
     │   订单入库                    │
     │                              │
     │                              ▼
     │                        ┌──────────┐
     └────────────────────────│ 下游服务  │
                              │  (库存)   │
                              │ ⑤ 消费消息 │
                              │ ⑥ 扣减库存 │
                              └──────────┘

7.2 三种实现方案

可靠消息模式有三种主流实现方案:

方案一:本地消息表(Local Message Table)

原理: 在业务数据库中额外创建一张"本地消息表",将业务操作和消息记录放在同一个本地事务中,保证两者原子性。然后通过定时任务扫描消息表,将消息发送到消息队列。

复制代码
┌────────────────────────────────────────────────────────────────┐
│                    本地消息表方案流程                             │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│  上游服务:                                                       │
│  ┌──────────────────────────────────────────────────┐           │
│  │ 数据库事务(原子性保证)                            │           │
│  │  1. 执行业务操作(如创建订单)                      │           │
│  │  2. 插入本地消息表记录(状态=待发送)                │           │
│  │  3. 提交事务                                      │           │
│  └──────────────────────────────────────────────────┘           │
│                                                                 │
│  定时任务(独立线程/进程):                                       │
│  ┌──────────────────────────────────────────────────┐           │
│  │ 1. 扫描本地消息表,查询状态=待发送的记录            │           │
│  │ 2. 发送消息到 MQ                                 │           │
│  │ 3. 发送成功 → 更新状态=已发送                      │           │
│  │ 4. 发送失败 → 重试(指数退避)                     │           │
│  └──────────────────────────────────────────────────┘           │
│                                                                 │
│  下游服务:                                                       │
│  ┌──────────────────────────────────────────────────┐           │
│  │ 1. 消费 MQ 消息                                  │           │
│  │ 2. 幂等性检查(根据消息ID判断是否已处理)           │           │
│  │ 3. 执行本地事务(如扣减库存)                      │           │
│  │ 4. 消费成功 → 确认消息                            │           │
│  │ 5. 消费失败 → 重试或进入死信队列                   │           │
│  └──────────────────────────────────────────────────┘           │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

本地消息表结构:

sql 复制代码
CREATE TABLE local_message (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    message_id VARCHAR(64) NOT NULL UNIQUE COMMENT '消息唯一ID',
    topic VARCHAR(128) NOT NULL COMMENT '消息主题',
    tag VARCHAR(128) COMMENT '消息标签',
    message_body TEXT NOT NULL COMMENT '消息体(JSON)',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-待发送, 1-发送成功, 2-发送失败',
    retry_count INT DEFAULT 0 COMMENT '重试次数',
    max_retry INT DEFAULT 10 COMMENT '最大重试次数',
    next_retry_time DATETIME COMMENT '下次重试时间',
    create_time DATETIME NOT NULL,
    update_time DATETIME NOT NULL,
    INDEX idx_status_next_retry (status, next_retry_time)
);

代码示例:

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;
    @Autowired
    private LocalMessageDao messageDao;

    // 创建订单 + 记录本地消息(同一个事务)
    @Transactional
    public void createOrder(OrderRequest request) {
        // 1. 业务操作:创建订单
        Order order = new Order();
        order.setOrderId(generateOrderId());
        order.setStatus("CREATED");
        orderDao.insert(order);

        // 2. 记录本地消息(与业务操作在同一事务中)
        LocalMessage message = new LocalMessage();
        message.setMessageId(generateMessageId());
        message.setTopic("ORDER_CREATED");
        message.setMessageBody(buildMessageBody(order));
        message.setStatus(0); // 待发送
        messageDao.insert(message);
    }
}

// 定时任务:扫描并发送消息
@Component
public class MessageSendTask {

    @Scheduled(fixedDelay = 5000) // 每 5 秒执行一次
    public void sendMessages() {
        // 查询待发送的消息
        List<LocalMessage> messages = messageDao.findByStatus(0, 100);
        for (LocalMessage msg : messages) {
            try {
                // 发送到 MQ
                rocketMQTemplate.send(msg.getTopic(), msg.getMessageBody());
                // 更新状态为发送成功
                messageDao.updateStatus(msg.getMessageId(), 1);
            } catch (Exception e) {
                // 更新重试信息
                messageDao.incrementRetry(msg.getMessageId(), calculateNextRetryTime(msg.getRetryCount()));
                log.error("消息发送失败: {}", msg.getMessageId(), e);
            }
        }
    }
}

// 下游服务:消费消息(幂等处理)
@Component
@RocketMQMessageListener(topic = "ORDER_CREATED", consumerGroup = "inventory_group")
public class InventoryConsumer implements RocketMQListener<String> {

    @Autowired
    private InventoryService inventoryService;

    @Override
    public void onMessage(String message) {
        OrderEvent event = JSON.parseObject(message, OrderEvent.class);
        String messageId = event.getMessageId();

        // 幂等性检查:是否已处理过
        if (inventoryService.isProcessed(messageId)) {
            return; // 已处理,直接返回
        }

        // 执行业务操作
        inventoryService.deductStock(event.getProductId(), event.getCount());

        // 标记已处理
        inventoryService.markProcessed(messageId);
    }
}
方案二:事务消息(Transactional Message)

原理: 利用消息中间件提供的事务消息能力(如 RocketMQ 的事务消息、Kafka 的事务消息),将消息发送分为两个阶段:先发送半消息占位,等本地事务执行完后,再确认提交或回滚。

复制代码
┌────────────────────────────────────────────────────────────────┐
│                 RocketMQ 事务消息流程                             │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ① 发送半消息(Half Message)                                    │
│     ┌──────────┐         ┌──────────┐                          │
│     │ Producer │────────►│ RocketMQ  │                          │
│     │          │         │  (半消息不可消费)│                      │
│     └──────────┘         └──────────┘                          │
│                                                                 │
│  ② 执行本地事务                                                   │
│     ┌──────────┐                                                │
│     │ Producer │ ──► 执行业务操作(如创建订单)                    │
│     │          │ ──► 成功或失败                                   │
│     └──────────┘                                                │
│                                                                 │
│  ③ 提交/回滚半消息                                                │
│     ┌──────────┐  成功→COMMIT   ┌──────────┐                   │
│     │ Producer │ ──────────────►│ RocketMQ  │                   │
│     │          │◄──────────────│          │                   │
│     └──────────┘  失败→ROLLBACK │  消息可消费│                   │
│                                 └──────────┘                   │
│                                                                 │
│  ④ 事务回查(如果步骤③因网络问题未到达 MQ)                         │
│     ┌──────────┐  回查本地事务状态  ┌──────────┐                 │
│     │ RocketMQ │ ────────────────►│ Producer │                 │
│     │          │◄────────────────│          │                 │
│     └──────────┘  返回提交/回滚     └──────────┘                 │
│                                                                 │
│  ⑤ 消费者消费消息                                                 │
│     ┌──────────┐         ┌──────────┐                          │
│     │ Consumer │◄────────│ RocketMQ  │                          │
│     │          │         │  (已提交消息)│                          │
│     └──────────┘         └──────────┘                          │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

RocketMQ 事务消息代码示例:

java 复制代码
// 生产者:发送事务消息
@Service
public class OrderTransactionProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    public void sendOrderCreatedMessage(Order order) {
        // 构建消息
        Message<String> message = MessageBuilder
            .withPayload(JSON.toJSONString(order))
            .build();

        // 发送事务消息
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
            "order_tx_group",           // 事务生产者组
            "ORDER_CREATED",            // Topic
            message,                    // 消息
            order                       // 附加参数,传给 executeLocalTransaction
        );

        if (result.getSendStatus() != SendStatus.SEND_OK) {
            throw new RuntimeException("事务消息发送失败");
        }
    }
}

// 事务监听器:执行本地事务 + 本地事务回查
@RocketMQTransactionListener(txProducerGroup = "order_tx_group")
public class OrderTransactionListener implements RocketMQLocalTransactionListener {

    @Autowired
    private OrderService orderService;

    // 执行本地事务
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        Order order = (Order) arg;
        try {
            // 执行本地事务:创建订单
            orderService.createOrder(order);
            // 本地事务成功 → 提交消息
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            // 本地事务失败 → 回滚消息
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    // 本地事务回查(MQ 长时间未收到 COMMIT/ROLLBACK 时触发)
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        String orderId = JSON.parseObject((String) msg.getPayload(), Order.class).getOrderId();
        // 查询本地事务执行结果
        Order order = orderService.getOrder(orderId);
        if (order != null && "CREATED".equals(order.getStatus())) {
            // 本地事务已成功执行 → 提交消息
            return RocketMQLocalTransactionState.COMMIT;
        } else {
            // 本地事务未执行或失败 → 回滚消息
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }
}
方案三:最大努力通知(Best Effort Notification)

原理: 上游服务完成本地事务后,通过消息队列或 HTTP 回调通知下游服务,下游服务处理失败时,上游服务会按一定策略(递增间隔、最大次数)多次重试通知,直到下游处理成功或达到最大重试次数。

复制代码
┌────────────────────────────────────────────────────────────────┐
│                  最大努力通知流程                                 │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│  上游服务完成本地事务                                              │
│    │                                                            │
│    ▼                                                            │
│  发送通知到下游服务                                                │
│    │                                                            │
│    ├── 成功 → 结束                                               │
│    │                                                            │
│    └── 失败 → 重试(间隔递增)                                    │
│         │                                                       │
│         ├── 第 1 次重试:1 分钟后                                 │
│         ├── 第 2 次重试:5 分钟后                                 │
│         ├── 第 3 次重试:10 分钟后                                │
│         ├── 第 4 次重试:30 分钟后                                │
│         ├── 第 5 次重试:1 小时后                                 │
│         │                                                       │
│         └── 超过最大重试次数 → 人工介入 + 告警                     │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

典型应用场景: 支付回调通知、短信验证码发送、异步任务结果通知

java 复制代码
// 最大努力通知实现
@Component
public class BestEffortNotificationService {

    @Autowired
    private RestTemplate restTemplate;

    // 发送通知,失败后异步重试
    public void notify(String callbackUrl, NotificationPayload payload) {
        try {
            restTemplate.postForEntity(callbackUrl, payload, String.class);
        } catch (Exception e) {
            // 异步重试
            retryTask.asyncRetry(callbackUrl, payload, 0);
        }
    }
}

@Component
public class RetryTask {

    private static final int[] RETRY_INTERVALS = {60, 300, 600, 1800, 3600}; // 秒
    private static final int MAX_RETRY = 5;

    @Async
    public void asyncRetry(String callbackUrl, NotificationPayload payload, int retryCount) {
        if (retryCount >= MAX_RETRY) {
            // 超过最大重试次数,记录异常,人工处理
            alertService.sendAlert("通知失败,需人工处理", callbackUrl, payload);
            return;
        }

        // 等待指定间隔后重试
        Thread.sleep(RETRY_INTERVALS[retryCount] * 1000);

        try {
            restTemplate.postForEntity(callbackUrl, payload, String.class);
            // 成功,结束
        } catch (Exception e) {
            // 继续重试
            asyncRetry(callbackUrl, payload, retryCount + 1);
        }
    }
}

7.3 三种方案对比

维度 本地消息表 事务消息(RocketMQ) 最大努力通知
实现复杂度 中 低(MQ 原生支持) 低
可靠性 高 高 中
实时性 中(依赖定时任务间隔) 高(准实时) 低(依赖重试间隔)
消息中间件依赖 低(任意 MQ) 高(需支持事务消息的 MQ) 低(任意 MQ 或 HTTP)
额外存储 需要(消息表) 不需要 需要(重试记录表)
适用场景 通用场景 需要高实时性的场景 允许一定延迟的异步通知

7.4 补偿机制(重点)

可靠消息模式的补偿机制与前面几种方案有本质区别:它不依赖回滚,而是依赖"正向重试"保证最终一致性。

核心补偿策略
复制代码
┌──────────────────────────────────────────────────────────────┐
│              可靠消息模式补偿机制                              │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│  1. 消息发送失败补偿                                           │
│     - 本地消息表:定时任务扫描未发送消息,持续重试               │
│     - 事务消息:MQ 通过事务回查确保消息被提交或回滚              │
│     - 最大努力通知:按递增间隔重试,直到成功或达到上限           │
│                                                               │
│  2. 消息消费失败补偿                                           │
│     - MQ 消费重试:consumer 返回失败时,MQ 自动重试             │
│     - 死信队列:重试超过上限后进入死信队列,人工处理             │
│     - 幂等性保证:消费端通过幂等性设计,保证重复消费不会         │
│       产生副作用                                               │
│                                                               │
│  3. 上游已成功、下游未成功的补偿                                │
│     - 这是可靠消息模式的核心补偿场景                             │
│     - 上游事务已提交,但下游消费失败或未消费到消息               │
│     - 补偿方式:通过重试投递 + 死信队列 + 人工对账              │
│                                                               │
│  4. 人工对账补偿(终极兜底)                                    │
│     - 定时对账:通过定时任务对比上下游数据差异                   │
│     - 数据修复:发现差异后自动或人工修复数据                     │
│     - 告警通知:异常数据通过告警通知运维人员                     │
│                                                               │
└──────────────────────────────────────────────────────────────┘
幂等性设计(关键)

可靠消息模式中,幂等性设计是补偿机制的核心。因为消息可能被重复投递,消费端必须保证幂等。

复制代码
幂等性实现方案:

1. 唯一约束法(数据库方案)
   ┌──────────────────────────────────────────────────┐
   │  CREATE TABLE inventory_deduction_log (           │
   │    id BIGINT AUTO_INCREMENT PRIMARY KEY,          │
   │    message_id VARCHAR(64) NOT NULL UNIQUE,  ← 唯一约束 │
   │    product_id VARCHAR(64),                        │
   │    count INT,                                     │
   │    create_time DATETIME                           │
   │  );                                               │
   │                                                   │
   │  消费逻辑:                                        │
   │  1. INSERT INTO inventory_deduction_log (message_id, ...) │
   │  2. 如果 INSERT 成功(无重复),执行业务操作         │
   │  3. 如果 INSERT 失败(唯一约束冲突),说明已处理,跳过│
   └──────────────────────────────────────────────────┘

2. Redis 去重法
   ┌──────────────────────────────────────────────────┐
   │  String key = "msg:deduction:" + messageId;       │
   │  Boolean exists = redis.setIfAbsent(key, "1",     │
   │                      24, TimeUnit.HOURS);          │
   │  if (!exists) {                                   │
   │      return; // 已处理过,跳过                      │
   │  }                                                │
   │  // 执行业务操作                                    │
   └──────────────────────────────────────────────────┘

3. 版本号法(乐观锁)
   ┌──────────────────────────────────────────────────┐
   │  UPDATE inventory                                │
   │  SET stock = stock - #{count},                   │
   │      version = version + 1                       │
   │  WHERE product_id = #{productId}                 │
   │    AND version = #{expectedVersion}              │
   │                                                  │
   │  如果 affected_rows == 0,说明版本已变,已处理过   │
   └──────────────────────────────────────────────────┘
死信队列与补偿
复制代码
消息消费失败 → 自动重试(1~16次,指数退避)

  ├── 重试成功 → 消费确认

  └── 重试全部失败 → 进入死信队列(Dead Letter Queue)
       │
       ├── 监控告警:通知运维人员
       │
       ├── 人工排查:分析失败原因
       │   ├── 业务逻辑错误 → 修复代码 → 重新消费
       │   ├── 数据依赖问题 → 修复数据 → 重新消费
       │   └── 临时故障 → 手动重试即可
       │
       └── 数据对账:定时任务对比上下游数据,修复差异

7.5 优缺点

优点:

优点 说明
高可用 上游和下游通过消息队列异步解耦,两者互不阻塞,可用性极高
高性能 异步处理,不锁定资源,吞吐量极高,适合高并发场景
松耦合 上下游服务仅通过消息交互,不直接依赖,易于独立扩展和部署
实现简单 不需要实现 Try/Confirm/Cancel 或补偿逻辑,只需保证消息可靠投递和幂等消费
流量削峰 消息队列天然支持流量削峰填谷,应对突发流量

缺点:

缺点 详细说明
实时性差 异步处理,数据存在延迟,不适合需要实时一致性的场景
数据不一致窗口 消息投递到消费处理之间存在时间窗口,期间数据可能不一致
下游失败处理复杂 下游消费失败后,只能重试或人工处理,无法"回滚"上游已提交的事务
依赖消息中间件 需要额外部署和运维消息队列,增加了系统复杂度
消息丢失风险 极端情况下(如 MQ 故障、磁盘损坏),消息可能丢失,需要额外保障
幂等性要求高 所有消费端必须实现幂等,否则重复消息会导致数据错误
排查困难 异步链路长,涉及多个组件,出问题时排查链路复杂

7.6 当前主流采用情况

可靠消息模式是互联网公司中应用最广泛的分布式事务方案之一,原因如下:

  1. 技术门槛低:不需要实现复杂的分布式事务协议,只需消息队列 + 幂等性设计
  2. 与异步架构天然契合:微服务广泛使用消息队列进行异步通信,可靠消息模式是自然延伸
  3. 性能优异:异步解耦,不阻塞,支持高并发、大流量

代表实践:

  • 几乎所有互联网公司都在使用:订单创建后异步通知各下游服务
  • RocketMQ 事务消息:阿里巴巴、字节跳动等广泛使用
  • Kafka 事务消息:国际公司偏好使用 Kafka 实现类似能力
  • 本地消息表:老系统改造或无法使用事务消息的场景

典型应用场景:

  • 订单创建 → 通知库存服务扣减库存
  • 用户注册 → 发送欢迎邮件/短信
  • 支付成功 → 通知订单系统更新状态
  • 数据变更 → 通知搜索引擎更新索引

八、阿里 Seata 分布式事务框架

8.1 概述

Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,前身是阿里内部的 TXC(Taobao Transaction Constructor)和 GTS(Global Transaction Service),于 2019 年 1 月正式开源。

Seata 提供了一套高性能、零侵入、一站式的分布式事务解决方案,支持 AT、TCC、Saga 和 XA 四种事务模式。

8.2 核心架构

复制代码
┌──────────────────────────────────────────────────────────────┐
│                       Seata 整体架构                           │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│  ┌──────────────────────────────────────────────────────┐    │
│  │                    Seata Server(TC)                   │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐            │    │
│  │  │ 事务协调   │  │ 锁管理    │  │ 会话管理  │            │    │
│  │  └──────────┘  └──────────┘  └──────────┘            │    │
│  │                                                       │    │
│  │  存储模式:File / DB / Redis                           │    │
│  │  集群部署:支持高可用                                   │    │
│  └──────────────────────────────────────────────────────┘    │
│       ▲                          ▲                           │
│       │                          │                           │
│  ┌────┴────────┐          ┌──────┴──────────┐               │
│  │  服务A(TM+RM)│          │  服务B(RM)      │               │
│  │              │          │                  │               │
│  │ @GlobalTransactio│      │ 执行SQL          │               │
│  │    nal       │          │ 注册分支事务      │               │
│  │ 调用服务B     │────────►│ 报告状态          │               │
│  │              │  RPC     │                  │               │
│  └──────────────┘          └──────────────────┘               │
│                                                               │
└──────────────────────────────────────────────────────────────┘

8.3 四种事务模式详解

模式一:AT 模式(默认)

原理: 基于本地事务 + 自动生成 undo 日志的两阶段提交

使用方式:

java 复制代码
// 只需加一个注解,零侵入
@GlobalTransactional(timeoutMills = 300000, name = "create-order")
public void createOrder(OrderRequest request) {
    // 业务逻辑:操作多个数据库
    orderService.save(order);      // 订单服务
    inventoryService.deduct(sku);  // 库存服务
    accountService.deduct(money);  // 账户服务
}

适用场景: 数据库操作、强一致性需求、希望零侵入的业务

模式二:TCC 模式

原理: 用户自定义 Try/Confirm/Cancel 接口

使用方式:

java 复制代码
// 定义 TCC 接口
@LocalTCC
public interface AccountTccService {

    @TwoPhaseBusinessAction(
        name = "deductAccount",
        commitMethod = "commit",
        rollbackMethod = "rollback"
    )
    boolean prepare(@BusinessActionContextParameter(paramName = "userId") String userId,
                    @BusinessActionContextParameter(paramName = "amount") BigDecimal amount);

    boolean commit(BusinessActionContext context);

    boolean rollback(BusinessActionContext context);
}

适用场景: 对一致性要求极高的场景(如金融、支付)、需要精确控制资源预留的场景

模式三:Saga 模式

原理: 编排器式 Saga,长事务拆分为多个本地事务,失败时逆序补偿

使用方式: 通过状态机 DSL(JSON)定义 Saga 流程

json 复制代码
{
  "Name": "orderSaga",
  "Type": "Saga",
  "StartState": "CreateOrder",
  "States": {
    "CreateOrder": {
      "Type": "ServiceTask",
      "ServiceName": "orderService.createOrder",
      "CompensateState": "CancelOrder",
      "Next": "DeductStock"
    },
    "CancelOrder": {
      "Type": "ServiceTask",
      "ServiceName": "orderService.cancelOrder",
      "End": true
    },
    "DeductStock": {
      "Type": "ServiceTask",
      "ServiceName": "inventoryService.deductStock",
      "CompensateState": "RestoreStock",
      "Next": "DeductBalance"
    },
    "RestoreStock": {
      "Type": "ServiceTask",
      "ServiceName": "inventoryService.restoreStock",
      "Next": "CancelOrder"
    },
    "DeductBalance": {
      "Type": "ServiceTask",
      "ServiceName": "paymentService.deductBalance",
      "CompensateState": "RefundBalance",
      "End": true
    },
    "RefundBalance": {
      "Type": "ServiceTask",
      "ServiceName": "paymentService.refundBalance",
      "Next": "RestoreStock"
    }
  }
}

适用场景: 长事务流程(如订单流程、审批流程)、需要异步处理、老系统改造(无需改造接口)

模式四:XA 模式

原理: 基于 XA 协议的 2PC,使用数据库的 XA 事务支持

使用方式:

properties 复制代码
# 配置文件中指定模式为 XA
seata.data-source-proxy-mode=XA
java 复制代码
@GlobalTransactional
public void createOrder() {
    // 业务逻辑
    // Seata 自动使用 XA 协议协调数据库事务
}

适用场景: 对一致性要求极高(CP 系统)、需要跨数据库厂商的标准化方案、传统企业应用迁移

8.4 四种模式对比

维度 AT 模式 TCC 模式 Saga 模式 XA 模式
一致性 最终一致 最终一致 最终一致 强一致
隔离性 读已提交 业务层隔离 无隔离 全局锁隔离
侵入性 零侵入 高侵入 中侵入 零侵入
性能 高 高 高 低
补偿方式 自动(undo 日志) 手动(Cancel 接口) 手动(补偿接口) 自动(数据库)
数据库要求 关系型数据库 无限制 无限制 支持 XA 的数据库
开发成本 低 高 中 低
运维成本 中(需 Seata Server) 中(需 Seata Server) 中(需 Seata Server) 中(需 Seata Server)
适用场景 通用业务 金融/支付 长事务/老系统 强一致性场景

8.5 Seata 的补偿机制(重点)

Seata 为不同模式提供了不同的补偿机制:

AT 模式补偿
复制代码
AT 模式补偿 = 自动 undo 日志回滚

1. 执行 SQL 时自动生成 undo 日志(前镜像 + 后镜像)
2. 全局回滚时,TC 通知各 RM 执行回滚
3. RM 根据 undo 日志生成反向 SQL
4. 回滚前校验数据一致性(后镜像匹配)
5. 回滚后删除 undo 日志

补偿特点:
- 自动补偿,无需开发者编码
- 有数据脏写校验
- 补偿失败时转为人工处理
TCC 模式补偿
复制代码
TCC 模式补偿 = 用户自定义 Cancel 方法

1. 协调者调用各服务的 Cancel 接口
2. 开发者需要处理:
   - 空回滚(Try 未执行时收到 Cancel)
   - 防悬挂(Cancel 先于 Try 到达)
   - 幂等性(Cancel 可能被重试)
3. Cancel 失败后重试,超时后人工处理

补偿特点:
- 业务层补偿,精确可控
- 需要处理空回滚、防悬挂、幂等
- 开发成本高但灵活性好
Saga 模式补偿
复制代码
Saga 模式补偿 = 逆序补偿 + 正向重试

1. 事务失败时,按逆序执行补偿
2. 补偿失败后重试,可配置重试策略
3. 支持正向重试(非致命错误时重试当前步骤)
4. 支持状态机驱动的自动补偿

补偿特点:
- 逆序执行补偿,保证事务完整性
- 支持状态机驱动的自动补偿
- 需要为每个正向操作编写补偿逻辑
XA 模式补偿
复制代码
XA 模式补偿 = 数据库 XA 协议回滚

1. 与 2PC 相同,使用数据库的 XA 事务
2. 回滚由数据库自动完成
3. 无需 Seata 额外处理补偿逻辑

补偿特点:
- 数据库自动回滚,最可靠
- 但性能最差,锁定时间长

8.6 Seata 的使用步骤

yaml 复制代码
# 1. 引入依赖
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.7.0</version>
</dependency>

# 2. 配置 Seata
seata:
  tx-service-group: my_tx_group
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848

# 3. 配置 undo_log 表(AT 模式必须)
CREATE TABLE undo_log (
  id BIGINT NOT NULL AUTO_INCREMENT,
  branch_id BIGINT NOT NULL,
  xid VARCHAR(128) NOT NULL,
  context VARCHAR(128) NOT NULL,
  rollback_info LONGBLOB NOT NULL,
  log_status INT NOT NULL,
  log_created DATETIME NOT NULL,
  log_modified DATETIME NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY ux_undo_log (xid, branch_id)
);

# 4. 业务代码中使用
@GlobalTransactional
public void createOrder(OrderRequest request) {
    // 正常业务逻辑
}

8.7 Seata 的优缺点

优点:

优点 说明
多模式支持 同时支持 AT、TCC、Saga、XA 四种模式,覆盖不同业务场景
零侵入(AT/XA) AT 和 XA 模式对业务代码零侵入,只需一个注解
高性能 AT 模式在第一阶段提交本地事务后释放本地锁,性能优于 2PC
生态完善 与 Spring Cloud、Dubbo、Nacos 等阿里生态深度集成
社区活跃 开源项目,社区活跃,版本迭代快,文档完善
企业级验证 在阿里内部经历了双十一等大流量考验

缺点:

缺点 说明
需要独立部署 Seata Server 增加了运维成本和复杂度,Seata Server 本身也是单点(需集群部署)
SQL 解析限制 AT 模式仅支持 INSERT、UPDATE、DELETE,不支持复杂 SQL
仅支持关系型数据库 AT 模式依赖数据库事务,不支持 NoSQL
全局锁开销 高并发下全局锁可能成为瓶颈
学习成本 配置项较多,概念较多,需要时间学习和调试
版本兼容性 版本升级时可能有 breaking changes,需要关注兼容性

8.8 当前主流采用情况

Seata 是目前国内最主流的分布式事务框架,原因如下:

  1. 阿里背书:经过阿里内部大规模验证,可靠性有保障
  2. 多模式覆盖:AT 模式零侵入适合快速接入,TCC 模式适合高要求场景,Saga 模式适合长事务
  3. 生态完善:与 Spring Cloud Alibaba、Dubbo、Nacos 等无缝集成
  4. 社区活跃:GitHub 上 25k+ Star,持续更新维护

国际对比:

特性 Seata Atomikos Narayana Bitronix
模式 AT/TCC/Saga/XA XA XA/Saga XA
零侵入 ✅(AT/XA) ❌ ❌ ❌
社区 活跃 一般 一般 已停止维护
国内使用 广泛 少 极少 极少

九、总结对比

9.1 各方案对比总表

维度 2PC 3PC TCC Saga AT 模式 可靠消息
一致性 强一致 强一致 最终一致 最终一致 最终一致 最终一致
隔离性 全局锁 全局锁 业务层 无 全局锁+本地锁 无
性能 低 低 高 高 高 极高
可用性 低 中 高 高 高 极高
侵入性 低 低 高 中 零 低
实现复杂度 低 中 高 中 低(框架实现) 中
补偿方式 自动 自动+超时 手动Cancel 手动补偿 自动undo 正向重试+对账
资源锁定 长时间 长时间 无 无 短时间(全局锁) 无
实时性 高 高 高 中 高 中
长事务支持 差 差 中 好 中 好
当前主流度 低 极低 高 高 高(国内) 极高

9.2 选型建议

复制代码
┌──────────────────────────────────────────────────────────────────┐
│                      分布式事务选型决策树                          │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│  需要强一致性?                                                    │
│  ├── 是 → 2PC/XA 或 3PC                                           │
│  │   ├── 可接受性能差 → 2PC/XA                                     │
│  │   └── 需要减少阻塞 → 3PC                                        │
│  │                                                                │
│  └── 否 → 最终一致性即可                                           │
│      ├── 需要实时一致性?                                          │
│      │   ├── 是 → 同步方案                                         │
│      │   │   ├── 希望零侵入?                                      │
│      │   │   │   ├── 是 → AT 模式(Seata)                         │
│      │   │   │   └── 否 → 继续判断                                 │
│      │   │   ├── 金融/支付场景?                                    │
│      │   │   │   ├── 是 → TCC 模式                                 │
│      │   │   │   └── 否 → 继续判断                                 │
│      │   │   └── 长事务/工作流/老系统改造?                          │
│      │   │       ├── 是 → Saga 模式                                │
│      │   │       └── 否 → AT 模式(默认推荐)                       │
│      │   │                                                        │
│      │   └── 否 → 可接受异步 + 延迟                                  │
│      │       ├── 已有 MQ 基础设施 → 事务消息(RocketMQ)             │
│      │       ├── 无 MQ 或简单场景 → 本地消息表                      │
│      │       └── 回调通知场景 → 最大努力通知                        │
│      │                                                            │
│      └── 混合场景 → Seata 多模式 或 可靠消息 + TCC 组合              │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘
场景 推荐方案 理由
通用互联网业务 Seata AT 模式 零侵入、高性能、易用
异步解耦场景 可靠消息模式 高性能、松耦合、天然适合微服务
金融/支付核心链路 TCC 模式 业务层精确控制,补偿可靠
长事务/审批流程 Saga 模式 无锁、适合长时间运行
传统企业应用 2PC/XA 强一致性、数据库原生支持
老系统改造 可靠消息(本地消息表)或 Saga 无需改造现有接口
高并发削峰 可靠消息模式 异步处理,消息队列天然削峰
混合场景 Seata 多模式 根据业务需要选择不同模式

9.3 最终建议

  1. 大多数互联网业务场景 :优先选择 Seata AT 模式,零侵入、高性能,能满足 90% 的业务需求
  2. 异步解耦/高并发削峰 :优先选择 可靠消息模式,利用消息队列实现高性能异步处理
  3. 资金安全要求高的场景 :选择 TCC 模式,虽然开发成本高,但对资金的精确控制是必要的
  4. 长事务/工作流流程 :选择 Saga 模式,无锁设计适合长时间运行的事务
  5. 避免使用 2PC/3PC:除非是传统企业应用或对强一致性有硬性要求,否则不建议使用
  6. 关键原则:能用 AT 就用 AT,需要异步解耦用可靠消息,不行再考虑 TCC 或 Saga,尽量避免 2PC

文档版本: v1.1

更新日期: 2026 年 7 月