Spring Cloud分布式事务与链路追踪详解

Spring Cloud分布式事务与链路追踪详解

定位:第 06 篇,讲透分布式事务问题与方案谱系(Seata 四模式、可靠消息)及链路追踪的原理与落地

适用版本:Spring Cloud 2023.x + Seata / Micrometer Tracing

说明:本篇只讲工程方案


目录


一、分布式事务问题

1.1 问题来源

复制代码
微服务原则:每服务独享数据库
一个业务(如下单)跨多个服务/库:
   订单服务写订单库 + 库存服务扣库存 + 积分服务加积分
单库事务(ACID)只能保证单库------跨库"要么都成要么都不成"没有现成机制

1.2 本质权衡

复制代码
强一致(分布式事务框架):协调成本高、性能差、可用性受影响
最终一致(消息/补偿):性能好,但有中间不一致窗口 + 复杂度(幂等/对账)

先问"是否真的需要事务":很多场景可以重新设计流程规避(如预扣、状态机、业务补偿),能用最终一致就不上强一致。


二、方案谱系

方案 一致性 侵入性 适用
2PC/XA 强 无(数据库层) 传统,性能差,少用
Seata AT 准强(自动补偿) 低(注解) 主流短事务
Seata TCC 强(业务自控) 高(三接口手写) 高要求核心链路
Seata SAGA 最终(补偿链) 中(定义补偿) 长事务、多步骤
可靠消息 最终 中 异步解耦场景

三、Seata AT 模式

3.1 三角色

复制代码
TC(Transaction Coordinator):事务协调器,独立服务,维护全局事务状态
TM(Transaction Manager):定义全局事务边界(@GlobalTransactional 标注处)
RM(Resource Manager):管理分支事务资源(各服务的数据库)

3.2 AT 模式流程(自动补偿)

复制代码
一阶段(业务执行):
   ① 解析 SQL,记录数据变更前后的镜像(undo log)
   ② 执行本地事务并提交(此时本地已提交,不持长锁)
   ③ 向 TC 注册分支

二阶段提交(顺利):
   TC 通知各分支 → 异步删除 undo log(快速,无锁)

二阶段回滚(某分支失败):
   TC 通知回滚 → 各分支用 undo log 反向补偿恢复数据

3.3 使用方式

java 复制代码
@GlobalTransactional   // 标注全局事务边界(如订单创建入口)
public void createOrder(OrderReq req) {
    orderService.create(...);     // 本地事务 1
    stockClient.decrease(...);    // Feign 调用 → 下游分支事务
    pointsClient.add(...);
}

业务几乎无侵入------这是 AT 流行的原因。

3.4 AT 的限制

复制代码
① 依赖数据库可记录镜像(SQL 可解析)
② 全局锁:写隔离依赖全局锁,并发热点有竞争
③ 脏读窗口:一阶段已提交,全局未决前他事务可见中间状态
④ 不适用于无法补偿的操作(已发出的短信、第三方扣款)

3.5 其他模式速览

模式 核心 场景
TCC Try-Confirm-Cancel 三接口手写 资源需精确控制、高性能核心链路
SAGA 每步正向操作 + 补偿操作,链式执行 长事务(跨多服务多步骤)
XA 标准 2PC 遗留系统,性能敏感场景不选

四、可靠消息最终一致

4.1 模型

复制代码
① 上游本地事务 + 发消息原子化(两种实现):
   本地消息表:事务里写消息表,后台任务投递(可靠但延迟)
   事务消息:MQ 原生支持(RocketMQ),半消息 → 提交/回查
② 下游消费消息执行业务
③ 幂等消费:重复投递不产生副作用(去重键/状态机)
④ 对账兜底:定时核对上下游数据,修复不一致

4.2 适用与取舍

复制代码
适合:异步可接受的场景(下单后加积分、发通知)
保证:最终一致(有中间不一致窗口)
必须配套:幂等 + 重试 + 对账,三者缺一会出资损/不一致

五、链路追踪

5.1 核心概念

概念 含义
Trace 一次完整请求链路(跨所有服务)
traceId 全链路唯一标识
Span 链路中的一跳(一次调用/操作),有 spanId 与父子关系
复制代码
[网关] → [服务A] → [服务B] → [DB]
 trace-abc
   span1    span2     span3    span4

5.2 Spring Cloud 的实现

复制代码
Micrometer Tracing:统一门面(Boot 3 集成)
   ↓ 桥接
Brave(Zipkin 系)或 OpenTelemetry
   ↓ 上报
Zipkin / Jaeger / OTel Collector → 可视化

5.3 传播机制

复制代码
出站:框架插桩把 traceId 注入请求头(traceparent / b3)
      ------ Feign/WebClient/消息生产者都被插桩(呼应 CL-02)
入站:下游从请求头恢复上下文,继续记录 span
异步:线程间上下文传递(装饰器/框架支持)

5.4 落地价值

复制代码
① 排障:一个请求慢/错,调用树定位是哪一跳
② 日志关联:日志带 traceId,从链路跳到日志
③ 依赖分析:服务拓扑与调用量统计

六、总结

  1. 问题本质:数据库独立后跨服务操作没有现成原子性;先问能否通过设计规避,再选方案。
  2. 方案谱系:强一致(2PC/Seata TCC)↔ 最终一致(消息)之间权衡;AT 模式低侵入是主流短事务方案。
  3. Seata AT:TC/TM/RM 三角色;一阶段本地提交 + undo 镜像,二阶段异步提交或反向补偿;限制是全局锁与脏读窗口。
  4. 可靠消息:本地事务 + 消息原子化 + 幂等消费 + 对账,适合异步最终一致场景。
  5. 链路追踪:traceId/span 模型,Micrometer Tracing 桥接实现;传播靠框架插桩自动注入/恢复;排障、日志关联、依赖分析三大价值。

七、常见高频面试题

1. 微服务为什么需要分布式事务?

要点:微服务原则是每服务独享数据库,一个业务操作(如下单)跨多个服务和库------订单、库存、积分各自独立事务。单库 ACID 只保证单库原子性,跨库"要么全成要么全不成"没有现成机制:本地都提交后某步失败,前面无法自动撤销。解决方向:强一致(分布式事务框架,如 Seata)或最终一致(可靠消息 + 补偿)。优先考虑重新设计流程规避跨服务事务。

2. 说说 Seata 的 AT 模式原理。

要点:三角色:TC(事务协调器,独立服务)、TM(定义全局事务边界,@GlobalTransactional)、RM(管理各服务分支事务)。流程:一阶段执行业务时解析 SQL 记录数据前后镜像(undo log),本地事务提交并注册分支(不持长锁);二阶段若全局提交,异步删除 undo(快速无锁);若回滚,用 undo 反向补偿恢复数据。特点:业务低侵入(一个注解),适合短事务;代价是全局锁竞争与一阶段提交带来的脏读窗口。

3. Seata 的 AT、TCC、SAGA 怎么选?

要点:AT------自动补偿、业务低侵入,适合常规短事务,但有全局锁与 SQL 可解析要求;TCC------Try/Confirm/Cancel 三接口手写,资源控制精确、性能好,但侵入大开发成本高,适合高性能核心链路(如支付);SAGA------长事务拆为多步正向操作 + 补偿操作,链式执行失败反向补偿,适合跨多服务的长流程。选择:短事务低侵入选 AT,高要求核心选 TCC,长流程选 SAGA。

4. 基于消息的最终一致怎么保证?要注意什么?

要点:模型:上游把"本地事务 + 发消息"原子化(本地消息表后台投递,或 RocketMQ 事务消息的半消息 + 回查);下游消费执行业务。三个必备配套:① 幂等消费------消息可能重复投递,用去重键/状态机保证重复无副作用;② 重试------消费失败重投直到成功或进死信人工处理;③ 对账------定时核对上下游数据兜底修复。代价是中间不一致窗口,适合异步可接受的场景(积分、通知)。

5. 为什么分布式事务方案都要考虑幂等?

要点:分布式场景重试不可避免------网络超时后重试、消息重复投递、补偿操作重放。若不幂等,重试会导致重复扣款/重复创建。幂等实现:唯一键去重(请求号/业务单号唯一索引)、状态机前置校验(已到目标态直接返回成功)、数据库原子操作。幂等是分布式正确性的基础设施,所有写接口都应具备,不只事务场景。

6. 什么是链路追踪?核心概念有哪些?

要点:记录一次请求跨所有服务的完整调用路径,用于排障与性能分析。概念:Trace 是一次完整链路,用全局唯一 traceId 标识;Span 是链路中的一跳(一次调用),有 spanId 和父子关系,记录耗时与状态;多个 Span 组成调用树。价值:定位慢/错在哪一跳、日志按 traceId 关联、生成服务依赖拓扑。

7. traceId 是怎么跨服务传递的?

要点:靠框架自动插桩。出站:调用方(Feign/WebClient/消息生产者)由追踪框架拦截,把当前上下文的 traceId/spanId 注入请求头(如 W3C traceparent 或 b3 头);入站:下游框架从请求头恢复上下文,新 Span 挂在父 Span 下继续传播;异步场景(线程切换)需上下文在线程间传递(装饰器或框架支持)。业务代码一般无感知,这依赖统一的追踪组件(如 Micrometer Tracing)。

8. Micrometer Tracing 是什么?和 Brave/Zipkin 什么关系?

要点:Micrometer Tracing 是 Spring 生态的追踪统一门面(类似 Micrometer 对指标的作用),屏蔽具体实现;底层桥接到 Brave(Zipkin 系)或 OpenTelemetry 做实际的上下文管理与上报;数据可上报到 Zipkin、Jaeger、OTel Collector 等后端存储与可视化。关系:Micrometer Tracing 是 API 层,Brave/OTel 是实现层,Zipkin/Jaeger 是后端。Boot 3 用这套替代了旧的 Spring Cloud Sleuth。

9. 什么场景适合用最终一致而不是分布式事务?

要点:判断标准是业务能否接受短暂不一致。适合最终一致:异步后续动作------下单后加积分、发通知、同步搜索索引、数据同步,这些晚几秒完成不影响主流程;强一致框架的协调成本与性能损耗不划算。必须强一致:资金类操作、库存扣减等中间状态会造成实际损失的。通用原则:主流程短事务强一致,衍生动作走消息最终一致 + 幂等 + 对账。

10. 下单流程涉及订单、库存、积分,你会怎么设计一致性?

要点:分而治之。① 核心路径(订单 + 库存)要强保护------可用 Seata AT/TCC,或更优的设计:库存预扣(下单扣减可用库存,支付超时回补),把跨服务事务收敛;② 衍生动作(积分、通知)走可靠消息最终一致------订单本地事务提交后发消息,积分服务幂等消费;③ 配套:幂等键(订单号)、超时补偿状态机(未支付订单超时关单回库存)、对账兜底。设计顺序:先尝试流程规避(预扣/状态机),再考虑事务框架,最后消息。

相关推荐
汤姆宝库1 小时前
预训练工程深拆:Scaling_Law、数据管线与分布式并行
分布式
pjj198541 小时前
Spark RDD 核心知识详解:算子、依赖与执行模型
大数据·分布式·spark
浔溺2 小时前
Spark核心基石:RDD全方位深度解析(原理、算子、实战、优化)
大数据·分布式·spark
catino3 小时前
RabbitMQ详解
分布式·rabbitmq·ruby
步行cgn3 小时前
Spring MVC POST 请求乱码:原因与完整解决方案
java·spring·mvc
爱吃火鸡面呀3 小时前
Spark RDD 创建与转换算子详解
大数据·分布式·spark
斌糖雪梨13 小时前
spring auth 核心流程详解
java·后端·spring
唠点键盘之外的18 小时前
29 切片的权限隔离(实战篇):Milvus + Spring AI,从 0 搭一条带权限过滤的检索链路
人工智能·spring·milvus
叶总没有会18 小时前
RabbitMQ高级
分布式·rabbitmq