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,从链路跳到日志
③ 依赖分析:服务拓扑与调用量统计
六、总结
- 问题本质:数据库独立后跨服务操作没有现成原子性;先问能否通过设计规避,再选方案。
- 方案谱系:强一致(2PC/Seata TCC)↔ 最终一致(消息)之间权衡;AT 模式低侵入是主流短事务方案。
- Seata AT:TC/TM/RM 三角色;一阶段本地提交 + undo 镜像,二阶段异步提交或反向补偿;限制是全局锁与脏读窗口。
- 可靠消息:本地事务 + 消息原子化 + 幂等消费 + 对账,适合异步最终一致场景。
- 链路追踪: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,或更优的设计:库存预扣(下单扣减可用库存,支付超时回补),把跨服务事务收敛;② 衍生动作(积分、通知)走可靠消息最终一致------订单本地事务提交后发消息,积分服务幂等消费;③ 配套:幂等键(订单号)、超时补偿状态机(未支付订单超时关单回库存)、对账兜底。设计顺序:先尝试流程规避(预扣/状态机),再考虑事务框架,最后消息。
