1. 分布式事务为何在微服务架构中成了“老大难”
1.1 从一次下单说起:你以为的一次事务,其实是三次
先看一个非常典型的电商下单流程。用户点了“提交订单”,后端要同时完成三件事:创建订单记录、扣减库存、从用户账户扣款。在单体应用时代,这三张表都放在同一个数据库里,一个 @Transactional 注解就能搞定。事务的 ACID 特性由数据库保证,要么全部成功,要么全部回滚,不会有中间状态。
但到了微服务架构下,订单服务、库存服务、账户服务各自独立部署,各自拥有独立的数据库。这时候一次下单请求,实际上是三次跨服务的远程调用。问题就来了:如果订单创建成功、库存扣减成功,但账户扣款失败,怎么办?订单服务和库存服务已经提交了本地事务,数据落库了,账户服务那边却回滚了。整体数据就处于不一致状态:用户没付钱,但订单下成功了,库存也扣了。
这就是分布式事务要解决的核心问题:在多个独立服务、独立数据库之间,如何保证一组操作的原子性。你可能觉得,那我在业务代码里依次调用,哪个失败了就手动调接口补偿行不行?比如账户扣款失败,就调订单服务的“取消订单”接口、调库存服务的“归还库存”接口。表面看可以,但一旦补偿调用本身失败了、或者网络超时了、或者服务重启了,整个流程就卡在了一个未知状态,没人知道哪些操作成功了、哪些失败了。分布式事务要解决的,正是这种“跨节点状态一致性”的难题。
1.2 CAP 定理:分布式事务的“不可能三角”
聊分布式事务,绕不开 CAP 定理。它说的是:一个分布式系统,在网络分区(Partition)发生时,一致性(Consistency)和可用性(Availability)只能二选一。绝大多数微服务架构在遭遇网络故障时,都会选择保证可用性,也就是服务还能响应,哪怕返回的是旧数据,等网络恢复后再把数据补齐。这就意味着,在微服务这种天然分布式的环境下,追求“强一致”的成本极高,通常只能退而求其次,追求“最终一致性”。
这个底层约束决定了分布式事务的设计思路和传统的数据库事务完全不同。传统数据库事务是同步的、强一致的:事务提交的那一刻,数据就必须是确定的。而分布式事务往往是异步的、带补偿机制的:允许中间出现短暂的不一致,但通过一系列后续操作,最终在某个时间点达到一致。
理解了这一点,你就能看懂为什么分布式事务领域有那么多方案,而且没有哪个方案是“银弹”——因为每个方案都是在 CAP 的不同取舍下,为特定业务场景服务的。
1.3 为什么不能直接把数据库事务改成分布式数据库事务
你可能想,能不能让所有微服务共用一个数据库,或者用分布式数据库(比如 TiDB、OceanBase)来解决?确实有团队这样做,对于数据量不大、并发不高的业务,这也是一个可选的方案。但微服务架构的核心价值之一,就是服务之间通过数据隔离来解耦。如果所有服务共享一个库,服务间的耦合度会急剧上升,一个慢查询就可能拖垮所有业务,而且数据模型演进时牵一发动全身。这种做法,等于把微服务又退化回单体了。
所以,在标准微服务架构下,服务间数据隔离是底线。分布式事务的问题,必须从架构层面、代码层面去解决,而不是一劳永逸地靠某个中间件“变魔法”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主力方案全盘点:从 XA 到 SAGA 的演进路线
2.1 最正统也最重的方案:XA 两阶段提交
先说说分布式事务的“祖宗方案”:XA 协议,也就是两阶段提交(2PC)。它的流程大致是:协调者先给所有参与者发送“准备”指令,参与者执行事务但不提交,把资源锁住,然后向协调者汇报“准备好了”;协调者收到所有参与者的“准备好了”之后,再广播“提交”指令;如果有任何一个参与者说“准备失败”,协调者就广播“回滚”指令。
这个方案最干净利落,因为它保证了全局的强一致性,而且对业务代码几乎无侵入。Java 领域有 JTA(Java Transaction API)标准,配合 Atomikos、Bitronix 这样的开源事务管理器,再加上 Spring 的 @Transactional 注解,就能实现分布式事务。
但问题是,两阶段提交在微服务场景下几乎不可用。为什么?第一,它需要在整个事务期间持有数据库锁,而跨服务的远程调用耗时不可控,锁持有时间可能长达数秒甚至更久,高并发场景下数据库连接池很快就会被耗尽。第二,协调者是单点,如果协调者宕机,所有参与者会一直卡在“已准备”状态,整个系统就僵住了。第三,对于跨语言、跨数据库类型的服务,XA 的支持程度参差不齐,落地成本很高。
所以在实际微服务架构里,直接用 XA 的团队非常少。它更多存在于面试题和教材里,作为理解分布式事务的起点。
2.2 用最终一致性换性能:本地消息表方案
本地消息表是早期互联网公司很常用的“朴素”方案。它的核心思想是:把发送消息这件事,和业务操作放在同一个本地事务里完成。
举个例子,订单服务创建订单时,在同一本地事务里,往本地消息表插入一条“待发送扣款消息”,然后提交;提交成功后,异步任务扫描消息表,把消息发到 MQ;账户服务消费消息,完成扣款。如果扣款成功,订单服务的消息处理任务就把消息状态标记为“已完成”;如果扣款失败,消息还在表里,可以配置重试,或者触发告警由人工介入。
这个方案的优点是原理简单、实现成本低,也无须引入额外的高阶中间件。缺点是:业务流程每多一个参与者,消息表就要增加一个状态字段;而且消息表最终还是会成为数据一致性的“兜底”,业务复杂之后消息表会变得非常庞大,维护成本随之上升。这套模式在面试中经常被提及,属于“必须知道但不见得愿意在生产中大范围用”的方案——因为现在有更成熟的替代品。
2.3 可靠消息事务:RocketMQ/Kafka 的事务消息
既然本地消息表的问题出在“消息表要自己维护”,那能不能把这个能力下沉到消息中间件里?这就是事务消息方案。RocketMQ 是其中最成熟的代表。
RocketMQ 的事务消息机制大致是:先发送一条“半消息”(broker 此时不会把消息投递给消费者),然后执行业务操作,最后向 broker 发送 commit 或 rollback 指令。如果业务操作执行期间进程挂了、或者 commit 指令没送达,broker 会主动回调生产者,询问“这个本地事务到底提交了没有”——这就是事务消息里著名的“回查”机制。通过回查,就能保证消息和业务操作最终状态一致。
Kafka 在 2.5 版本之后也有了自己的事务 API,核心是 initTransactions()、beginTransaction()、sendOffsetsToTransaction() 这些方法,但它主要解决的是“消费生产一体”的精确一次处理语义,和 RocketMQ 面向业务消息的事务消息模型不太一样,使用起来也更底层。如果团队技术栈是 Kafka 且不想引入新中间件,也可以用,但要做好复杂度的心理准备。
事务消息方案在**“先本地操作,再异步通知下游”**的场景下非常好用。比如订单创建后发消息通知积分服务给用户加积分,这种场景下的失败率极低,回查机制也基本不会触发。但它不适用于强一致的场景——如果下游消费失败,数据最终可能不一致,还是需要一个兜底的对账或重试机制。
2.4 柔性事务的扛把子:TCC 模式
TCC 是 Try-Confirm-Cancel 三个单词的缩写。它的核心是把一个业务操作拆成三个阶段:
- Try:资源预留。比如扣库存,先不真正扣减,而是把“预占库存”数量加一,标记该订单占用了 N 件库存。这个阶段不改变实际可用库存,但对业务方来说,资源已经被“锁定”了。
- Confirm:确认执行。如果所有参与的服务的 Try 阶段都成功了,就进入 Confirm 阶段,把这些预留的资源真正扣减。
- Cancel:取消。如果任何一个服务 Try 失败,就进入 Cancel 阶段,把预留的资源释放掉。
TCC 的优点是:它从业务层面管理事务,而不是靠数据库锁,所以性能比 XA 好得多;同时它又能做到比最终一致性方案更精确的控制,几乎可以做到同步地返回“成功”或“失败”。缺点也很明显:每个业务都要实现三段逻辑,开发量巨大。而且你还需要处理各种边界情况:Confirm 和 Cancel 必须设计成幂等的——因为网络超时后框架会重试,同一个 Confirm 请求可能被发两次,你不能重复扣库存。
在实际项目中,TCC 通常用于资金类、订单类的核心链路,因为这类场景对一致性的要求高,而且业务本身比较容易拆成预占、确认、取消三段。像 Seata 框架中的 TCC 模式、以及 ByteTCC 这类开源组件,都是这个方案的落地实现。
2.5 长事务的归宿:Saga 模式
Saga 模式最早是一篇 1987 年的数据库论文提出的概念,近几年在微服务领域焕发新生。它的思想非常简单:把一个长事务拆成一系列子事务,每个子事务都有对应的补偿操作。执行时按顺序执行子事务,一旦某个子事务失败,就依次逆向执行前面所有子事务的补偿操作,把状态回滚。
Saga 有两种落地形态:编排式(Choreography) 和 协同式(Orchestration)。
编排式 Saga 没有中心协调者,每个服务完成后,通过消息队列通知下一个服务继续执行。比如订单服务完成后发消息,库存服务收到后开始扣库存,扣完成后再发消息给账户服务,依此类推。这个模式的优点是服务间解耦度极高;缺点是业务流程分散在多个服务的消息处理逻辑里,出了问题时非常难排查——你都不知道现在流程走到哪一步了。
协同式 Saga 则引入一个 Saga 编排器(或者叫流程引擎),由它统一编排各步骤,记录每一步的执行状态,并在失败时触发补偿。这个模式的优点是流程清晰可控、便于监控;缺点是编排器本身会成为一个逻辑上的中心点,需要保证它具备高可用。
Java 生态里,开源的有 Apache ServiceComb Saga(已进入 Apache 基金会)、Seata 的 Saga 模式,商业的有 Temporal、Camunda 等。Saga 特别适合业务流程长、涉及服务多、对一致性要求是“最终一致”的场景。
2.6 打包好的一站式方案:Seata
说到 Java 生态的分布式事务,绕不开阿里巴巴开源的 Seata(Simple Extensible Autonomous Transaction Architecture)。Seata 一个框架里几乎把所有主流模式都做了整合:AT 模式(自动补偿)、TCC 模式、SAGA 模式、XA 模式。它内部有三个核心组件:TC(事务协调器,独立部署的服务端)、TM(事务管理器,嵌入在业务代码里)、RM(资源管理器,嵌入在微服务的数据库访问层)。
Seata 的 AT 模式,是它最有代表性的亮点。它对业务代码几乎是零侵入的:你只需要在方法上加一个 @GlobalTransactional 注解,Seata 会自动拦截 SQL,记录数据变更前后的快照,在全局事务提交时把变更同步到各分支事务,在全局事务回滚时用快照恢复数据。这背后靠的是 TC 的统一协调,以及 RM 自动生成的 undo_log 表。对于不想写 TCC 三段逻辑、又不希望引入消息队列做最终一致性的团队来说,Seata AT 模式是快速落地的首选。
3. 代码实操:用 Spring Boot + Seata 演示订单与库存的分布式事务
3.1 目标:复现经典的扣库存场景
为了把抽象的分布式事务落到代码上,我用一套最小但完整的 Spring Boot + Seata 演示来走一遍。场景还是那个经典案例:订单与库存。有两个微服务:order-service 和 stock-service,它们各自有一个数据库(本地用 MySQL)。业务流程是:调用 order-service 的下单接口,order-service 本地插入一条订单记录,然后通过 FeignClient 调用 stock-service 的扣库存接口;两个操作必须同时成功或同时失败。
这个 demo 我选 Seata 的 AT 模式,因为它最能体现“微服务像单体一样写事务”的开发体验。如果你已经理解 AT 模式的工作原理,后面想换成 TCC 或 Saga 也是一样的思路。
3.2 环境准备:启动 Seata Server
Seata Server(也就是 TC)需要独立部署。目前 Seata 1.7.x 是稳定的版本,我选择 Docker 方式部署快速验证:
bash复制docker run --name seata-server -p 8091:8091 \
-e SEATA_IP=127.0.0.1 \
-e SEATA_PORT=8091 \
seataio/seata-server:1.7.0
默认情况下 Seata Server 将配置信息存储在本地文件里,并用内置的 file 模式注册发现;对于本地 demo 完全够用。如果是生产环境,建议把注册中心换成 Nacos,把配置中心也放到 Nacos 里,配合数据库存储事务会话,即可实现 Seata Server 的高可用。
Seata 还有一个需要重视的点:它的事务协调逻辑依赖全局事务 ID 的传播。order-service 发起全局事务后,会生成一个 XID(全局事务 ID),这个 XID 需要传递到 stock-service,stock-service 的 RM 才能把本地事务链路挂到同一个全局事务下。在 Spring Cloud 场景下,Seata 提供了 SeataHandlerInterceptor,会自动把 XID 注入到 Feign 调用的 Header 中;也就是说,只要你的服务是标准的 Spring Cloud Feign 调用链,XID 的传递是开箱即成的,不需要自己处理。
3.3 依赖配置:order-service 和 stock-service 的准备
两个服务的核心依赖基本一致:
xml复制<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
注意:spring-cloud-starter-alibaba-seata 会自动引入 Seata 客户端依赖,里面已经包含了 seata-spring-boot-starter。你需要额外配置 application.yml:
yaml复制seata:
enabled: true
application-id: order-service
tx-service-group: my_test_tx_group
registry:
type: file
config:
type: file
这里我偷了个懒,注册中心和配置中心都用 file 模式,服务端也是默认的。如果注册中心用 Nacos,那么 registry.type 换成 nacos,再加上 Nacos 的地址和服务名配置即可,大同小异。
还有一个关键:AT 模式要求数据库中创建 undo_log 表。Seata 官方提供建表 SQL,你需要在订单库和库存库各执行一遍:
sql复制CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
3.4 业务代码:加一个注解,事务就生效了
order-service 的下单入口,代码非常朴素:
java复制@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StockFeignClient stockFeignClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(OrderRequest request) {
// 1. 本地插入订单
Order order = new Order();
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setStatus("CREATED");
orderMapper.insert(order);
// 2. 远程扣减库存
stockFeignClient.deduct(request.getProductId(), request.getQuantity());
}
}
关键就是那个 @GlobalTransactional 注解。它在方法开始执行时,会向 TC 注册一个全局事务,生成 XID;随后方法体内执行的所有本地 SQL(不管在哪个服务),都会通过 RM 自动把分支事务挂到这个全局事务下面。stock-service 侧的 deduct 方法,就是一个普通的 @Transactional 本地事务:
java复制@Service
public class StockService {
@Autowired
private StockMapper stockMapper;
@Transactional(rollbackFor = Exception.class)
public void deduct(Long productId, Integer quantity) {
Stock stock = stockMapper.selectByProductId(productId);
if (stock.getAvailable() < quantity) {
throw new BusinessException("库存不足");
}
stockMapper.deductStock(productId, quantity);
}
}
看到没有:stock-service 不需要感知自己是“分布式事务的一部分”,它只管自己的本地事务。全局的事务协调和回滚,全部由 Seata 在底层完成。
3.5 验证回滚:抛异常后到底发生了什么
现在做最关键的验证:在 stock-service 的扣库存逻辑里,手动抛出一个运行时异常,模拟库存不足或系统异常。然后调用下单接口,观察结果。
正常情况下,你会看到:order-service 的订单表里没有新增数据,stock-service 的库存表也没有减少。这就是 Seata AT 模式做的工作:stock-service 抛异常后,本地事务回滚,RM 将分支事务标记为失败并上报 TC;TC 发现全局事务有分支失败,就会通知 order-service 的 RM 执行逆向补偿——用 undo_log 里保存的前镜像 beforeImage 把订单数据恢复原状,完成全局回滚。
你还可以在 MySQL 里开启 general_log 来观察执行的 SQL:可以看到 Seata 在执行业务 SQL 的同时,会多出几条查询 beforeImage、写入 undo_log 的 SQL 记录。这正是 AT 模式“自动补偿”的底气——它在你无感知的情况下做了数据快照,回滚就是靠这些快照“翻旧账”。
Seata AT 模式的运行机制一定要吃透,因为这是面试的高频考点,也是实际运维分布式事务时的核心知识。
4. 方案选型决策:从 CAP 到真实业务场景的权衡
4.1 一致性要求有多高:强一致还是最终一致
每次有人问我“分布式事务到底该选哪种方案”,我的第一个反问永远是:你的业务真的需要强一致吗?大多数业务其实只需要最终一致性。
如果你的业务场景是“用户下单后要扣库存”,短时间的超卖可以接受吗?如果不行,比如你是卖限量版球鞋,库存只有 20 双,多卖一单都是事故,那至少要在这单交易上做强制校验——这种情况下,订单扣减和库存扣减应该是同步完成的,可以选 TCC 或 Seata AT。
如果业务是“用户下单后发优惠券、加积分”,这类业务晚十秒到达用户基本无感,而且本身失败率极低。在这种情况下,用事务消息 + 重试 + 对账即可,没必要上 TCC 这类高成本方案,杀鸡用牛刀反而给自己增加维护负担。
4.2 性能要求:本地消息还是同步事务
分布式事务方案的成本和性能差异是量级的。这里我直接给一张对比表,方便你尽快做筛选:
| 方案 | 一致性强度 | 吞吐量影响 | 业务侵入度 | 典型工具 | 适用场景 |
|---|---|---|---|---|---|
| XA / 2PC | 强一致 | 高(锁时间长) | 低 | Atomikos, JTA | 简单且短链路,服务内跨库事务 |
| TCC | 灵活控制 | 中 | 高(每个业务都要写 Try/Confirm/Cancel) | Seata TCC, ByteTCC | 资金、库存等核心链路强管控 |
| Seata AT | 强一致(自动补偿) | 中 | 极低 | Seata | 快速落地、对锁竞争不敏感的业务 |
| Saga 编排式 | 最终一致 | 中 | 中(流程引擎配置) | Seata Saga, Temporal | 长流程、多服务,如订单整套生命周期 |
| Saga 协同式 | 最终一致 | 高(异步解耦好) | 中 | 自研基于 MQ | 服务间彻底解耦,异步链路过长 |
| 事务消息 | 最终一致 | 高 | 低 | RocketMQ, Kafka | 异步通知,下游可重试 |
| 本地消息表 | 最终一致 | 中 | 中 | 自研 | 不想引额外中间件的团队 |
从表格能看出来,方案的“侵入度”和“一致性强度”往往是反相关的。想要强一致,就要接受更多业务开发成本;想要低侵入低成本,就要放弃强一致,接受最终一致。没有两全其美。
4.3 团队能力和基础设施存量
除了业务诉求,还要考虑团队现状。如果你的团队刚接触微服务,还没有任何分布式事务的经验,我的建议是先从最终一致性入手,用 RocketMQ 事务消息把第一条链路跑通;不要一上来就上 Seata AT,因为你可能还不清楚 AT 模式在极端情况下会带来什么运维负担。
如果你的团队已经有比较成熟的 DBA 和消息中间件运维能力,那可以尝试 Seata。但要记住,Seata Server 本身也是一个需要高可用保障的基础组件,它一旦出问题,所有接入的业务都受影响,这需要明确的基础设施责任归属。
4.4 一次技术选型的实战走查
我去年做过一个技术咨询项目,客户的业务是“订单下单后同步拆单到多个仓库发货”。最初的方案是:订单服务先落库,然后通过 MQ 通知仓储服务,按最简单的最终一致性方案走。后来上线后发现一个问题:在 618 大促期间,部分订单在仓储服务消费失败后,消息重试了 3 次依然失败,订单就一直挂在“待发货”状态,运营需要每天手动对账处理,人工成本很高。
后来我们把方案改成了 SAGA 协同式:引入一个轻量的流程编排器,把“创建订单 → 分配仓库 → 通知发货 → 扣减预留库存”串成一个流程,每步都记录了执行状态,失败时自动执行补偿。因为流程较长、涉及的服务多,Saga 提供的“可见性”带来了巨大的排障优势——现在任何一个环节出了问题,编排器都能告诉你卡在哪一步,补偿执行到哪一步。这个调整并没有接入任何复杂的分布式事务框架,只是在业务代码里加了一个流程状态推进和补偿接口,但效果立竿见影。
这个案例想说明的是:选型不是选择题,而是判断题。分布式事务方案没有绝对的好坏,只有合不合适。
5. 实战避坑:真实项目中比书本知识更重要的经验
5.1 补偿操作必须幂等:否则回滚会放大事故
分布式事务中,几乎所有方案最终都依赖重试。Seata AT 模式的回滚会重试、事务消息的消费端会重试、TCC 的 Confirm/Cancel 会重试、Saga 的补偿步骤也会重试。一旦遇到超时或网络抖动,框架会自动重试同一个操作。如果你的补偿接口不是幂等的,比如扣库存的补偿执行了两次,就会把用户库存扣成负数。
幂等设计最常用的手段是:在执行前先查询业务单据状态,只有“待补偿”状态才执行补偿,执行后把状态改成“已补偿”。或者使用唯一业务键,配合数据库唯一索引做防重。这个细节看似简单,但在设计 TCC 和 Saga 补偿时,十个人里有八个会漏掉。
5.2 依赖 Seata 前的风险提示:undo_log 的清理,别等出事了再做
Seata AT 模式会往每张业务表对应的 undo_log 表写入快照数据。事务结束后,这些数据按理说应该被删除,但高并发下偶尔会有残留,或者长期运行后堆积非常严重。如果不定期清理,undo_log 表会变得巨大,影响数据库性能和 Seata 的查询效率。所以接入了 Seata 的团队,务必建立定时清理 undo_log 数据表的任务,例如每小时删除“已成功且创建时间超过 1 小时”的记录,但要小心不要误删活跃事务的数据。
另外,AT 模式靠锁和快照保证隔离性,Seata 默认会在数据操作上加一个全局锁(主要依赖数据库行锁)。高并发下,如果不同事务同时操作同一行数据,可能会出现锁等待,甚至死锁。这点在生产环境一定要压测验证。如果发现锁冲突严重,要么考虑把冲突热点数据拆分,要么干脆换 TCC 模式,把锁控制权拿回业务层。
5.3 别被“全局事务”四个字骗了:AT 模式不是万能的
Seata AT 模式的确很省事,但它不是银弹。它依赖于对方 SQL 能被 Seata 的 RM 自动解析。如果你的业务 SQL 里使用了 update ... join、复杂的存储过程、临时表操作,Seata 可能无法正确计算前后镜像,导致无法回滚或回滚出错。
还有一些场景不能用 AT 模式,比如:直接调用第三方 HTTP 接口、发送短信、调用外部支付网关。这些是“外部系统”,Seata 的 RM 根本管不到它们。一个全局事务里如果混入了外部系统调用,就必须在业务层手工处理补偿逻辑,或者把外部调用的结果异步化,避免参与 Seata 的事务管理。切记:AT 模式只对你能控制的数据源生效。
5.4 监控分布式事务的诀窍:盯着每个环节的“卡点”
分布式事务最大的痛点是排障困难——业务挂了,但不知道挂在哪一步。我的经验是:每个参与分布式事务的服务,都要对关键步骤输出结构化日志,至少包含全局事务 ID(XID)、本地事务 ID、分支事务 ID、当前步骤状态。这样当出现不一致时,你能通过 XID 把所有节点的日志串在一起,一眼定位是哪个服务、哪个 API 卡住了。
如果你用 Seata,它的 TC 端会记录全局事务的会话状态,Seata 也提供了 Dashboard(如 seata-server 的 console),可以直接查看活跃事务和历史事务的状态。这是一个非常实用的排障入口。
5.5 最后实践建议:先从“局部一致”开始
如果一个系统从单体拆到微服务,有几十个业务链路都涉及跨服务的数据一致性,不要试图一口气全部上分布式事务框架。我建议的做法是:先梳理业务,把链路分级——核心资金链路用强一致方案,非核心链路直接用 MQ 异步化。然后从最痛的那条链路开始,把方案跑通、监控跑通、应急预案跑通,再逐步推广。分布式事务不是越早引入越好,而是越需要时才引入。
我见过太多团队被“分布式事务”这个词吓住,然后在项目初期就引入了一整套 Seata 或 Sage 引擎,最后发现很多链路其实用不到,反而白白增加了一大堆复杂度,团队还因为不熟悉而频繁踩坑。分布式事务的终极目标,是让业务不至于因为“分布式”这三个字而当机,而不是为了让架构图更好看。
