HOOOS

微服务分布式事务如何解决?告别手动补偿的成熟模式与框架

0 11 码农小Q 微服务分布式事务数据一致性
Apple

你提到的“线上环境微服务数据不一致,特别是在复杂业务流程中,每次都手动补偿”的问题,确实是微服务架构中的一个老大难问题,也是分布式系统设计中绕不开的挑战。很高兴你开始寻找成熟的模式来系统性解决它,而不是止步于“手动补偿”这种高风险、低效率的做法。

在微服务环境中,由于服务独立部署、独立数据库,传统单体应用中的 ACID 事务(如XA事务)在分布式场景下往往力不从心,因为它会严重影响性能和可用性,且实现复杂。因此,我们通常会转向追求“最终一致性”(Eventual Consistency)。

以下是一些解决分布式事务问题的成熟模式和框架,它们能有效减少甚至消除手动补偿的需要:

1. Saga 模式

Saga 模式是一种处理长时运行事务的模式,它将一个分布式事务分解为一系列本地事务。每个本地事务都有一个对应的补偿事务。如果在任何一个本地事务失败,可以通过执行之前已完成事务的补偿事务来回滚整个 Saga。

核心思想:
Saga 模式不保证强一致性,而是通过一系列正向操作和反向补偿操作来最终达成一致。

两种实现方式:

  • 编排(Orchestration)模式:

    • 引入一个中央协调器(Orchestrator),负责管理 Saga 的整个生命周期。协调器发出命令给各个服务执行本地事务,并根据执行结果决定下一步操作(继续下一个事务或触发补偿事务)。
    • 优点: 业务逻辑集中,流程清晰,易于监控和管理。
    • 缺点: 协调器可能成为单点故障和性能瓶颈,逻辑变得复杂,如果流程变动频繁,维护成本高。
    • 适用场景: 业务流程相对固定且复杂,需要统一协调的场景。
  • ** Choreography(编舞)模式:**

    • 没有中央协调器。每个服务在完成自己的本地事务后,通过发布事件通知下一个参与服务的启动,或者在失败时发布补偿事件。服务之间通过事件进行通信和协作。
    • 优点: 服务之间解耦度高,没有单点故障,系统更具弹性。
    • 缺点: 业务流程分散在各个服务中,难以追踪和理解整个 Saga 流程,增加了调试和维护的难度。
    • 适用场景: 业务流程相对简单,或者对去中心化、高可用性有强需求的场景。

实现框架/工具:

  • Seata: 阿里巴巴开源的分布式事务解决方案,支持 AT、TCC、Saga 和 XA 模式,其中 Saga 模式提供了编排式的解决方案。

2. TCC (Try-Confirm-Cancel) 模式

TCC 模式是一种两阶段提交的柔性事务解决方案。它将一个业务操作分成三个阶段:

  • Try(尝试): 尝试执行业务,完成所有业务检查,预留必要的业务资源。这一阶段并非实际执行,而是“锁定”或“预占”资源。
  • Confirm(确认): 真正执行业务操作,不进行任何业务检查,只使用 Try 阶段预留的资源。
  • Cancel(取消): 在业务执行失败时,释放 Try 阶段预留的资源。

核心思想:
TCC 模式要求业务操作具有幂等性,并允许手动拆分业务操作。通过 Try 预留资源,Confirm/Cancel 实际操作/释放资源,保证分布式事务的原子性。

优点:

  • 隔离级别高:在 Try 阶段就对资源进行预占,避免了脏读、幻读等问题。
  • 数据强一致性:在分布式事务的层面上,通过 Try-Confirm-Cancel 保证了业务的最终一致。
  • 业务侵入性适中:需要改造业务代码,但相比 Saga 模式,流程更显式。

缺点:

  • 业务改造复杂:每个参与服务都需要实现 Try、Confirm、Cancel 三个接口,对业务代码有较强的侵入性。
  • 实现成本高:需要考虑各种异常情况,如网络超时、宕机等,确保 Confirm/Cancel 的幂等性。

实现框架/工具:

  • Seata: 同样支持 TCC 模式,提供了 TCC 事务管理器,降低了实现复杂度。

3. 基于可靠消息队列的最终一致性

这是在微服务中最常用的一种模式,尤其适用于对实时性要求不是极高的场景。

核心思想:
当一个服务完成本地事务后,会向消息队列发送一个消息。其他需要参与分布式事务的服务订阅这个消息,接收到消息后执行自己的本地事务。如果消息发送失败或处理失败,可以通过消息队列的重试机制来保证消息的最终送达和处理,从而达到最终一致性。

常用流程:

  1. 生产者事务: 服务 A 完成本地事务,并将一个“待发送”消息写入本地消息表(与业务数据在同一事务中)。
  2. 消息发送: 消息发送者定期扫描本地消息表,将消息发送到消息队列。
  3. 消费者事务: 服务 B 从消息队列接收消息,执行本地事务。
  4. 消息确认: 服务 B 处理完成后向消息队列发送确认(ACK)。
  5. 消息清理: 服务 A 收到消息队列的发送成功通知后,删除本地消息表中的对应消息。

优点:

  • 服务解耦:生产者和消费者通过消息队列进行通信,相互独立。
  • 高吞吐量:异步处理,提高了系统的并发能力。
  • 易于扩展:新的服务可以很方便地加入或退出。
  • 可靠性高:消息队列通常提供持久化、重试、死信队列等机制,保证消息不丢失和最终送达。

缺点:

  • 最终一致性:不能保证实时一致性,数据可能在一段时间内处于不一致状态。
  • 难以追溯:事务流程分散在多个服务和消息队列中,排查问题相对复杂。

实现框架/工具:

  • Apache RocketMQ, Apache Kafka, RabbitMQ 等消息队列都可以作为实现最终一致性的基础。结合本地消息表事务消息(如 RocketMQ 的事务消息) 可以更可靠地实现。

如何选择?

  • Saga 模式: 适用于长流程、业务复杂、对实时一致性要求不高,但需要保证最终一致性的场景。如果业务流程会频繁变动,编排模式可能会带来高维护成本。
  • TCC 模式: 适用于对数据一致性要求较高,且业务逻辑可以清晰地划分为 Try、Confirm、Cancel 三个阶段的场景。例如资金账户操作等。但其侵入性最强,开发成本也最高。
  • 可靠消息队列: 最常用且推荐的方式。适用于大多数对实时一致性要求不高,可以接受最终一致性的场景。例如订单创建后通知库存扣减、积分发放等。实现相对简单,系统解耦性最好。

总结

解决微服务中的数据不一致问题,核心是拥抱“最终一致性”的设计哲学,并根据业务场景选择合适的柔性事务模式。这些模式能够将复杂的分布式事务分解为一系列可管理的本地事务,并通过补偿或异步通知机制来保证数据的最终一致,从而避免了每次都依赖人工介入。

推荐你先从基于可靠消息队列的最终一致性开始实践,它最普适且易于上手。对于更复杂的、对实时性有一定要求的关键业务,可以考虑引入 SagaTCC 模式,但请务必评估其带来的开发和维护成本。

点评评价

captcha
健康