[特殊字符] 分布式事务中,@GlobalTransactional 与 @Transactional 到底怎么配合用?

在微服务架构中,随着系统模块的拆分,单体应用中的本地事务已经无法满足跨服务的数据一致性需求。此时,我们就需要引入分布式事务解决方案,比如 Seata。在使用 Seata 的过程中,很多人会遇到一个常见的疑问:

💬"我在调用方加了 @GlobalTransactional,服务提供方还需要加 @Transactional 吗?"

答案是:必须要加!

这篇文章我们就深入讲清楚:为什么服务提供方还需要加 @Transactional,以及两者如何协作实现真正可靠的分布式事务。


🧩 一、事务的两种类型

在讨论这个问题之前,我们先要明确两个事务注解的含义。

✅ 1. @GlobalTransactional(Seata 分布式事务)
  • 加在调用方法上,表示开启一个全局事务

  • 属于 Seata 的 AT 模式 事务注解,由 Seata 的 Transaction Coordinator (TC) 负责协调。

  • 它会自动传播全局事务上下文给远程调用的服务。

✅ 2. @Transactional(Spring 本地事务)
  • 加在某个服务的方法上,表示方法内部的数据库操作需要 在同一个本地事务中进行

  • 只有方法内部的操作在同一个本地事务中,Seata 才能在全局事务失败时,正确地让这些操作回滚。


📌 二、真实业务场景还原

我们以一个真实的订单系统为例:

👇 业务流程:
  1. 用户在订单服务(A)中下单。

  2. 系统同时调用库存服务(B)扣减库存。

  3. 库存服务还会记录一条扣减日志到另一个表。

❗ 错误示范(服务B没加 @Transactional):
复制代码
// A服务
@GlobalTransactional
public void createOrder() {
    orderMapper.insert(order);
    inventoryFeign.decreaseStock(); // 远程调用 B 服务
}

// B服务
public void decreaseStock() {
    stockMapper.decrease();   // 成功
    logMapper.insertLog();    // 失败,抛异常
}

这时候会发生什么?

  • A服务使用 @GlobalTransactional,Seata 启动了全局事务。

  • B服务没有使用 @Transactional,所以两个表的操作并不在同一个本地事务中

  • 如果 logMapper.insertLog() 抛出异常,虽然 Seata 会通知回滚,但由于 stockMapper.decrease() 已经提交,数据就不一致了!

✅ 正确示范(服务B加上 @Transactional):
复制代码
// B服务
@Transactional
public void decreaseStock() {
    stockMapper.decrease();   // 与下方操作属于同一个本地事务
    logMapper.insertLog();
}

现在当 logMapper.insertLog() 抛出异常时:

  • Spring 会将整个 decreaseStock 方法回滚。

  • Seata 能检测到异常,通知所有参与者事务回滚。

  • 整个流程变成原子性的。


🔍 三、Seata 的本质:全局事务 + 本地事务协同

Seata 做的不是"接管"你的数据库事务,而是:

在每个服务节点中使用代理数据源(DataSourceProxy),通过拦截本地事务的提交/回滚操作,来实现 全局的一致性控制

因此:

  • @GlobalTransactional 控制"何时开始、是否提交全局事务"

  • @Transactional 保证"本地数据库操作的一致性和可回滚性"

你不加 @Transactional,Seata 就没法让你的数据库事务回滚,因为根本没事务!


🛠 四、项目接入建议

如果你要使用 Seata 进行分布式事务控制,务必注意以下几点:

配置项 说明
✅ @GlobalTransactional 放在业务流程发起方(一般是 Controller 或 Service)
✅ @Transactional 放在服务提供方处理数据库操作的 Service 方法上
✅ 使用 Seata 的 DataSourceProxy 所有数据源都要通过 Seata 的代理包装
✅ 注册到 Seata Server 服务注册中心需正确配置 Seata 服务

📚 五、总结

@GlobalTransactional 是分布式事务的起点,@Transactional 是本地事务的保障,二者缺一不可。

在微服务的世界里,要实现数据一致性,必须让每个参与者都对自己的事务负责。Seata 会协调所有本地事务的提交和回滚,但它不会、也无法替你加上事务控制。


如果你觉得这篇文章对你有帮助,欢迎点赞、评论、收藏 ✅

有任何问题也欢迎私信或留言交流!


📌 附推荐阅读:

相关推荐
卡皮巴拉爱吃小蛋糕4 分钟前
MySQL的事务(Transaction)【学习笔记】
数据库·笔记·学习·mysql
兔子蟹子5 分钟前
Java 实现SpringContextUtils工具类,手动获取Bean
java·开发语言
jackson凌18 分钟前
【Java学习方法】终止循环的关键字
java·笔记·学习方法
Kyrie_Li20 分钟前
Kafka常见问题及解决方案
分布式·kafka
种时光的人30 分钟前
Java多线程的暗号密码:5分钟掌握wait/notify
java·开发语言
祢真伟大34 分钟前
dmncdm达梦新云缓存数据库主从集群安装部署详细步骤说明
服务器·数据库·缓存
爱玩电脑的L1 小时前
MYSQL-库的基本操作
数据库·mysql
Apifox1 小时前
Apifox 4月更新|Apifox在线文档支持LLMs.txt、评论支持使用@提及成员、支持为团队配置「IP 允许访问名单」
前端·后端·ai编程
我家领养了个白胖胖1 小时前
#和$符号使用场景 注意事项
java·后端·mybatis
寻月隐君1 小时前
如何高效学习一门技术:从知到行的飞轮效应
后端·github