seata 分布式事务回滚失败主因是异常未传至协调器,需避免全局异常处理器吞异常、业务方法静默捕获、异步调用丢失上下文,并确保分支注册成功、rollbackfor 配置正确及通过日志和数据库验证真实回滚。

Spring Boot 本身不直接处理分布式事务回滚异常,它只管理本地数据库事务。真正要让跨服务的操作“一起成功或一起失败”,必须引入分布式事务框架(如 Seata、LCN、Atomikos),并重点解决异常能否被正确捕获和传播这一核心问题。
关键不是“怎么写回滚代码”,而是“异常能不能传到事务协调器”
分布式事务框架(比如 Seata)靠拦截 @GlobalTransactional 方法,在方法抛出异常时触发全局回滚。但如果异常在到达拦截器前就被吃掉了,框架就认为“执行成功”,自然不会回滚。
- 全局异常处理器(
@RestControllerAdvice)提前捕获并返回了友好提示 → Seata 没看到异常 → 提交事务 - 业务方法里
try-catch住了异常,没再throw出去 → AOP 代理收不到异常 → 事务不回滚 - 调用远程服务时用了异步(
@Async)或线程池 → 当前线程事务上下文丢失 → 分布式事务链路断裂
Seata 场景下最典型的回滚失败原因及对策
以你正在用的 Seata 1.4.2 为例,订单+库存这种典型场景中,回滚异常往往不是“配置错”,而是“流程断”:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
-
别让全局异常处理器抢在 Seata 前面:把
@ExceptionHandler的捕获范围缩小,避开业务异常类(如InventoryNotEnoughException),或者在处理前手动触发回滚(GlobalTransactionContext.getCurrent().rollback()) -
确保异常能穿透到 Seata 拦截器:业务方法内不要
catch后静默吞掉,该throw就 throw;自定义异常建议继承RuntimeException -
检查分支事务是否真正注册成功:查看 Seata Server 日志中是否有
branch register记录,没有说明服务未正确接入或 TM/RM 配置有误
检查 rollbackFor 和 noRollbackFor 是否反向生效
虽然 Seata 主要看全局事务状态,但本地 @Transactional 的配置仍会影响分支事务行为:
- 如果分支服务的方法加了
@Transactional(noRollbackFor = BusinessException.class),而它又抛出了这个异常 → 本地不回滚,Seata 会认为“这步成功了”,整个全局事务可能提交 - 推荐统一显式声明:
@Transactional(rollbackFor = Exception.class),避免因检查异常默认不回滚导致分支数据残留 - 用 DEBUG 日志确认行为:
logging.level.io.seata=DEBUG,观察GlobalTransaction.rollback()是否被调用
验证回滚是否真实发生,不能只看接口返回
很多问题表面是“没回滚”,实际是“回滚了但你看不见”:
- 查数据库:直接连库存库,看扣减操作是否被撤销(注意 Seata 的 AT 模式会生成 undo_log 表,回滚后记录应被清理)
- 看 Seata 控制台:访问
http://seata-server:8091,查看全局事务状态是否为Rollbacked - 模拟网络异常:停掉库存服务,让 RPC 调用超时或抛出
TimeoutException,这类异常默认属于运行时异常,Seata 应能捕获并回滚










