微服务架构下的分布式事务实践:从 2PC 到 Saga 的演进选型

举报
yd_286050264 发表于 2026/10/30 00:03:31 2026/10/30
【摘要】 微服务拆分后,原本一次本地事务就能保证的 ACID 特性被跨库、跨进程调用打破。本文对比 2PC、TCC、Saga、本地消息表四种主流分布式事务方案的取舍,并以编排式 Saga 为例给出可恢复、幂等补偿的 Java 实现与落地建议。

微服务架构下的分布式事务实践:从 2PC 到 Saga 的演进选型

一、从单体到微服务:事务边界被打破

在单体架构中,一个业务操作往往对应一次本地数据库事务,ACID 特性由数据库引擎直接保证。但当系统拆分为微服务后,一次"下单"操作可能跨越订单、库存、账户三个独立数据库,原本一条 BEGIN ... COMMIT 就能搞定的事,变成了跨进程、跨网络的协作问题。

分布式事务的核心矛盾在于:强一致性与可用性不可兼得。跨服务调用随时可能因网络抖动、节点宕机而失败,若坚持同步阻塞式提交,系统吞吐将被严重拖累。

二、四种主流方案对比

方案 一致性 性能 复杂度 适用场景
2PC/XA 强一致 低 中 同源异构数据库
TCC 强一致 中 高 资金类核心业务
Saga 最终一致 高 中 长流程业务编排
本地消息表 最终一致 高 低 异步解耦场景

两阶段提交(2PC)通过协调者锁定所有参与者资源再统一提交,能保证强一致,但同步阻塞、协调者单点故障会导致整个事务长时间挂起,在微服务高并发场景下几乎不可用。

TCC(Try-Confirm-Cancel)把每个操作拆成三个阶段,业务侵入性强,需要为每个接口实现确认与补偿逻辑,开发成本高,但对资金扣减等必须强一致的场景仍是首选。

三、Saga 模式实战

Saga 将长事务拆分为一串本地事务 T1, T2, ..., Tn,每个 Ti 配套一个补偿操作 Ci。任一 Ti 失败时,按序执行 Ci-1, ..., C1 回滚。相比 TCC,Saga 只需正向与补偿两套逻辑,复杂度更低。

以"下单 → 扣库存 → 扣余额"为例,基于编排式(Orchestration)Saga 的核心实现:

@Service
public class OrderSagaOrchestrator {

    @Autowired private OrderService orderService;
    @Autowired private InventoryClient inventoryClient;
    @Autowired private AccountClient accountClient;
    @Autowired private SagaLogRepository sagaLog;

    public void execute(CreateOrderCmd cmd) {
        Long sagaId = sagaLog.start(cmd);
        try {
            // T1: 创建订单(PENDING 状态)
            Long orderId = orderService.create(cmd);
            sagaLog.forward(sagaId, "createOrder", orderId);

            // T2: 扣减库存
            inventoryClient.deduct(cmd.getSkuId(), cmd.getQty());
            sagaLog.forward(sagaId, "deductInventory", cmd.getQty());

            // T3: 扣减余额
            accountClient.charge(cmd.getUserId(), cmd.getAmount());
            sagaLog.forward(sagaId, "chargeAccount", cmd.getAmount());

            orderService.markPaid(orderId);
            sagaLog.complete(sagaId);
        } catch (Exception e) {
            compensate(sagaId, e);
        }
    }

    // 逆序执行已完成步骤的补偿
    private void compensate(Long sagaId, Exception e) {
        SagaState state = sagaLog.getState(sagaId);
        if (state.done("chargeAccount"))   accountClient.refund(cmd);
        if (state.done("deductInventory")) inventoryClient.restore(cmd);
        if (state.done("createOrder"))     orderService.cancel(state.orderId);
        sagaLog.abort(sagaId, e.getMessage());
    }
}

关键设计要点:

  1. Saga 日志持久化:每步执行前先写日志,保证宕机重启后能按日志恢复并继续补偿,这是实现幂等与可恢复的基础。
  2. 补偿操作必须幂等:网络重试可能导致同一补偿被执行多次,refund、restore 等接口需通过业务流水号去重。
  3. 补偿不可失败:补偿本身若失败,只能重试加告警,不能无限嵌套回滚,必要时转人工介入。

四、选型建议

  • 资金、库存等强一致核心域:优先 TCC,宁可牺牲吞吐也要保证不超卖、不丢钱。
  • 订单履约、跨服务流程编排:选 Saga,最终一致加补偿,吞吐与一致性兼顾。
  • 简单异步通知、日志埋点:本地消息表加 MQ 重试即可,无需引入重型事务框架。

五、总结

分布式事务没有银弹。从 2PC 的强一致阻塞,到 Saga 的最终一致补偿,本质是在一致性强度与系统吞吐之间做权衡。落地时先问自己:这个业务真的需要强一致吗?大多数场景下,可观测的最终一致加完善的补偿与告警机制,比脆弱的强一致更值得信赖。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。