分布式-跨服务事务一致性的常见解决方案

Spring Cloud + OpenFeign 架构下,服务间调用导致的分布式事务一致性问题

通用、改动少

背景

  • 单体应用下,Spring 的本地事务可以保证一次业务操作的数据一致性。
  • 当系统拆分为多个服务(微服务架构)后,一个业务流程可能涉及多个服务,各自有自己的数据源和事务边界。这时本地事务已无法跨服务协作,出现事务一致性难题。
  • OpenFeign 作为 HTTP 客户端,仅负责服务间 HTTP 调用,不具备事务能力。

常见的解决方案

分布式事务一般有三大主流解决方案:

A. 柔性事务(最终一致性)

1. 可靠消息+本地事务(推荐)

  • 典型代表:
    • 阿里巴巴 RocketMQ 事务消息、淘宝 TCC-Transaction,或者使用第三方组件(如 Seata)。
  • 思路:
    • 服务A完成本地事务后,通过消息中间件将事件通知给服务B,服务B异步消费消息并操作,然后回调确认。
    • 追求最终一致性,即允许服务间在"短时间内"状态不一致,但最后会达成一致
  • 典型场景:
    • 订单创建→扣库存,订单服务本地提交事务→发送消息→库存服务消费扣减库存→最终两者一致
  • 优点:业务系统只需在关键点集成下消息发送和消费,改动有限
  • 适用:绝大多数互联网场景。
  • 具体的实现流程:
    1. 本地业务操作+发送消息 同步事务提交
      • 订单服务执行"创建订单",同时写入一条"待发送消息"到本地消息表,两步在同一个本地事务中完成
    2. 事务成功后,消息通过任务(或轮询机制)推送到消息队列(RabbitMQ/RocketMQ/Kafka)
    3. 下游服务(如库存服务)监听消息,完成本地业务操作并记录消费成功(幂等、可重试)。
    4. 消费成功后发确认消息(可以回查、补偿,如有失败可人工或自动补回)

核心思想:只要本地数据库和消息发送是一致的(在一个事务里),就保证跨服务的最终一致

  • 相关实现:
    • RocketMQ 事务消息天然支持这种模式(发送半消息→确认之后再正式发出)。
    • 使用RabbitMQ不能天然保证半事务,需要本地消息表+定时补偿投递机制
      • mysql本地表 + 消息投递+retry 框架
      • 也开源的 outbox pattern 组件:如 axon、dtm、spring-transation-mq 等
    • Spring Cloud Stream 结合可靠消息表 + 事务模板(见阿里GTS,京东pop,滴滴dtm等实现)。

2. TCC(Try-Confirm-Cancel)

  • 通过补偿机制、幂等接口来实现一致性
  • 推荐开源实现:tcc-transaction
  • 优点:强一致,资源持锁时间短,性能优异
  • 缺点:侵入性较大,需要业务方实现Try/Confirm/Cancel三个接口

B. 强一致性(事务性)

1. 分布式事务中间件

  • Seata
  • Seata实现了三种主流模式:
    • AT模式 (自动代理数据库操作,适合简单场景)
    • TCC模式(适合复杂业务)
    • SAGA模式(适合长事务/批量处理)。
  • 注意事项
    • Spring Cloud 已有较好的Seata集成:Spring Cloud Alibaba Seata
    • Spring Boot/Spring Cloud下集成Seata后,只需要在业务方法上加@GlobalTransactional 即可跨服务实现事务一致,其实就是AT模式
    • 改动较小,大体保持原业务不变,只需数据库框架(mybatis/jpa)保证被代理。
  • Seata AT 模式原理(自动代理)
    • 适用场景:仅限于关系型数据库,适合有独立数据表、无复杂跨库 join 的场景。
    • 工作流程
      1. 全局事务TM :业务方法加@GlobalTransactional,自动开启分布式事务
      2. 分支事务RM :各微服务参与该全局事务,Seata通过数据源代理,自动拦截SQL,记录前镜像(before image)后镜像(after image)。这些数据用于回滚操作。
      3. 协调者TC:全局事务协调者,维护事务的提交/回滚状态。
      4. 提交时 ,TC通知各分支事务commit,各自提交; 回滚时,TC通知rollback,各自利用before image反向生成回滚SQL。
    • 优势:无需侵入业务代码,适合99%业务场景;适合写已提交/读未提交隔离级别。
    • 缺点:不适合复杂SQL、批处理、长耗时业务。
  • Seata TCC模式
    • 定义Try/Confirm/Cancel三个操作,由应用实现,Seata负责调用和回调。
    • 代码实现复杂度高,但能做到强一致且性能尚可。
  • Seata SAGA模式
    • 业务解耦拆解为本地事务+补偿事务。
    • 适合长耗时、弱一致性、多步骤业务

C. SAGA模式(长事务)

什么是SAGA

  • 将全局大事务拆成一系列本地事务,每个本地事务完成后马上提交。
  • 如果后续任一事务失败,走补偿逻辑回滚已完成的事务(非物理回滚,而是业务反向操作)。
  • 适合"长事务"和业务流程编排型服务。

举个例子

  1. 步骤A(比如订单服务减余额):成功,变更已生效
  2. 步骤B(扣库存服务):如果失败,调用A的补偿(比如给用户余额+回去)
  3. 类似流程一直下去,任何环节失败就补偿先前步骤。

两种实现方式

  • 协同式 (Choreography):

    • 原理: 没有中央协调者。每个服务完成自己的本地事务后,发布一个事件 (Event),触发链中的下一个服务执行其本地事务。失败时,同样通过发布失败事件来触发补偿流程,由监听该事件的服务执行自己的补偿事务。
    • 通信: 通常依赖事件总线消息队列 (如 RabbitMQ, Kafka)。
    • 例子:
      • 订单服务创建订单 (T1),发布 OrderCreated 事件。
      • 库存服务监听 OrderCreated,扣减库存 (T2),发布 StockDecreased 事件。
      • 支付服务监听 StockDecreased,处理支付 (T3)。如果支付失败,发布 PaymentFailed 事件。
      • 库存服务监听 PaymentFailed,执行补偿操作:增加库存 (C2),发布 StockRestored 事件。
      • 订单服务监听 StockRestoredPaymentFailed,执行补偿操作:取消订单 (C1)。
    • 优点: 服务松耦合,简单直观(对于简单流程)。
    • 缺点:
      • 流程分散: 事务逻辑散布在各个服务中,难以跟踪和调试整个 Saga 的状态。
      • 循环依赖风险: 服务间可能形成事件监听的循环依赖。
      • 难以理解复杂流程: 当 Saga 涉及的服务和步骤增多时,事件链会变得非常复杂。
  • 编排式 (Orchestration):

    • 原理: 引入一个中央协调器 (Orchestrator)Saga 执行协调器 (SEC)。该协调器负责调用各个服务的本地事务接口,并根据执行结果决定是调用下一个服务的本地事务,还是调用前面服务的补偿事务接口。协调器维护整个 Saga 的状态。
    • 通信: 协调器直接向参与方服务发送命令式消息(如 RPC 调用,或通过 MQ 发送命令消息)。
    • 例子:
      • Saga 协调器调用订单服务的 createOrder (T1)。
      • 成功后,协调器调用库存服务的 decreaseStock (T2)。
      • 成功后,协调器调用支付服务的 processPayment (T3)。
      • 如果 processPayment 失败,协调器会依次调用库存服务的 increaseStock (C2) 和订单服务的 cancelOrder (C1)。
    • 优点:
      • 集中控制: 事务逻辑和状态管理集中在协调器,易于理解、监控和调试。
      • 职责分离: 参与方服务只需关注自己的业务逻辑和补偿逻辑,不需知道整个 Saga 流程。
      • 避免循环依赖: 协调器控制流程,不易产生服务间循环依赖。
    • 缺点:
      • 协调器复杂性: 协调器本身的设计和实现可能比较复杂,需要处理状态持久化、失败恢复等。
      • 协调器单点风险: 协调器可能成为性能瓶颈或单点故障(需要考虑高可用)。
      • 额外组件: 引入了额外的协调器组件。

注意点

  • 必须为每个可能需要回滚的本地事务设计补偿逻辑
  • 补偿事务必须是幂等的,因为可能被重复调用
  • Saga 不保证隔离性,在 Saga 执行过程中,系统可能处于中间状态(部分事务已完成,部分未完成),只能保证最终会达到一致状态(要么全部完成,要么全部补偿)
  • 对于简单、步骤较少 的 Saga,协同式可能更简单快捷
  • 对于复杂、步骤多、需要清晰状态管理 的 Saga,编排式通常是更好的选择。可以使用 Seata Saga 模式实现、Spring Statemachine 或专门的 Saga 编排框架

如何选型

针对场景

  • 极高一致性(资金流水、金融系统、券商):建议选 TCC 或 Seata(AT/TCC);最大化减少分布式环节,有条件时局部使用XA
  • 高一致+业务较简单:Seata-AT
  • 业务复杂、有补偿/逆操作能力:SAGA
  • 简单易落地,允许短时不一致:可靠消息+本地事务(消息表+RabbitMQ补偿)

针对改动量

  • 以Seata为例,可以做到对现有代码结构最小改动,主要是增加注解和配置,不必重写业务逻辑
  • 如果服务间调用是同步,事务度要求很高,Seata AT模式最合适
  • 如果可以异步补偿,可靠消息方案或SAGA也考虑。

收尾

微服务分布式事务不是银弹,无需一刀切,优先设计幂等+补偿机制,尽可能"弱化"事务

确实有强一致场景时,推荐首选Seata,次之为可靠消息,TCC/SAGA视业务复杂性酌情采用。

相关推荐
IvanCodes17 分钟前
五、Hadoop集群部署:从零搭建三节点Hadoop环境(保姆级教程)
大数据·hadoop·分布式
熊大如如1 小时前
Java 反射
java·开发语言
猿来入此小猿1 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo2 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder2 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
pjx9872 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
炒空心菜菜3 小时前
SparkSQL 连接 MySQL 并添加新数据:实战指南
大数据·开发语言·数据库·后端·mysql·spark
Panesle3 小时前
分布式异步强化学习框架训练32B大模型:INTELLECT-2
人工智能·分布式·深度学习·算法·大模型
多多*3 小时前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle
爱喝酸奶的桃酥3 小时前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql