您的位置: 首页> Java源码> 从 “库存飞了” 到 “事务稳了”:后端 er 必通的分布式事务 & Seata 闯关指南

从 “库存飞了” 到 “事务稳了”:后端 er 必通的分布式事务 & Seata 闯关指南

时间:2025-09-08 13:45:02 来源:互联网

今天就从 “踩坑经历” 出发,把分布式事务、CAP 定理、Seata 这些知识点掰开揉碎讲 —— 保证不用晦涩术语,看完就能上手!

一、先复习:单机事务是 “老熟人” ACID

咱写单体应用时,事务就是个 “靠谱管家”:你让它干 “扣余额 + 生成订单”,要么全成,要么全撤,绝不搞 “余额扣了订单没生成” 的骚操作。这背后就是 ACID 四兄弟:

但自从拆了微服务,麻烦就来了 —— 订单在订单服务的库,库存在库存服务的库,单机事务管不了两个库啊!这就轮到分布式事务登场了。

二、分布式事务:微服务时代的 “新麻烦”

简单说,分布式事务就是 “跨服务、跨数据库” 的事务。比如用户下单的完整流程:

  1. 订单服务:创建订单(订单库);

  2. 库存服务:扣减库存(库存库);

  3. 支付服务:扣用户余额(用户库)。

这三步只要有一步失败,前面成功的就得回滚 —— 总不能 “订单创建了,库存没扣,最后超卖” 吧?

但问题是:三个服务不在一个库,单机事务的 ACID 没法跨库生效。这时候就需要专门的方案来管,而聊方案前,绕不开的就是 CAP 定理。

三、CAP 定理:鱼和熊掌的 “选择题”

CAP 是分布式系统的 “铁律”,说的是三个特性里,你最多能同时满足两个:

image.png

为啥不能三者兼得?举个例子:
假如库存服务和订单服务断网了(触发 P):

在分布式系统中,分区容错性(P)是必须要保证的,我们必须做出取舍 所以实际开发中,咱得根据场景选 “CP” 或 “AP”:

1. CP 模式:认死 “数据一致”,牺牲点可用性

适合 “数据错了比用不了更严重” 的场景,比如银行转账、库存扣减。
典型例子:ZooKeeper。它会保证所有节点数据一致,但如果主节点挂了,要等从节点选主,这期间服务不可用。

2. AP 模式:认死 “服务能用”,接受暂时不一致

适合 “用不了比数据错了更严重” 的场景,比如商品列表、用户点赞。
典型例子:Eureka。服务注册信息可能暂时不一致(比如某个服务下线了,个别节点还显示在线),但永远能给你返回结果,不会卡着不动。

四、Seata 登场:分布式事务的 “救火队员”

知道了 CAP 的取舍,那具体怎么实现分布式事务?阿里开源的 Seata 就是咱后端 er 的 “神器”—— 它把复杂的分布式事务逻辑封装好,咱不用自己写一堆协调代码。

先搞懂 Seata 里的三个 “关键角色”,记成 “项目组分工” 就好:

角色作用类比
TM(事务管理器)发起和结束事务(比如订单服务说 “开始下单事务”“结束事务”)项目经理:负责启动项目、宣布项目成败
RM(资源管理器)管理具体的数据库资源(比如库存服务负责扣库存、回滚库存)开发工程师:负责干具体活,听指挥回滚
TC(事务协调器)居中协调 TM 和 RM(比如告诉库存服务 “该回滚了”)项目协调员:盯着进度,出问题了协调大家补救
image.png

简单说:TM 喊 “开始”,RM 们干活,TC 盯着,一旦有 RM 报错,TC 就让所有人回滚;全成了,TC 就宣布 “事务成了”。

五、微服务集成 Seata:手把手教你 “搭环境”

光说不练假把式,咱以 Spring Cloud 项目为例,三步集成 Seata:

1. 先装 TC(事务协调器)

2. 微服务端配置(以订单服务为例)

第一步:加依赖

xml

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

第二步:配 application.yml

告诉服务 “TC 在哪”“用什么事务模式”:

yaml

seata:
  tx-service-group: my_tx_group  # 事务组名,要和TC配置一致
  service:
    vgroup-mapping:
      my_tx_group: default  # 映射到TC的集群名
    grouplist:
      default: 127.0.0.1:8091  # TC的地址和端口
  registry:
    type: nacos  # 注册中心类型
    nacos:
      server-addr: 127.0.0.1:8848  # Nacos地址

第三步:加注解

在事务发起的方法上(比如订单服务的 “创建订单” 方法)加@GlobalTransactional

java

@Service
public class OrderService {
    // 调用库存服务、支付服务
    @Autowired
    private InventoryFeignClient inventoryClient;
    @Autowired
    private PaymentFeignClient paymentClient;

    // 全局事务注解:这是TM的核心操作
    @GlobalTransactional(rollbackFor = Exception.class)
    public void createOrder(OrderDTO orderDTO) {
        // 1. 创建订单(本地事务)
        orderMapper.insert(orderDTO);
        // 2. 调用库存服务扣库存(远程事务)
        inventoryClient.deduct(orderDTO.getProductId(), orderDTO.getCount());
        // 3. 调用支付服务扣余额(远程事务)
        paymentClient.deduct(orderDTO.getUserId(), orderDTO.getAmount());
    }
}

搞定!这样三个服务的事务就被 Seata 管起来了。

六、Seata 事务模式:选对姿势才 “高效”

Seata 支持三种模式,各有优劣,咱按 “常用程度 + 重点掌握” 排序:

1. AT 模式:性价比之王(重点掌握!)

2. XA 模式:严谨但 “慢”

3. TCC 模式:灵活但 “麻烦”

最后:你踩过分布式事务的坑吗?

其实分布式事务没那么玄乎 —— 记住 “CAP 取舍是前提,Seata 是工具,AT 模式先上手”,大部分场景都能搞定。

你之前有没有遇到过 “库存飞了”“余额扣了订单没生成” 的坑?或者用 Seata 时踩过版本兼容的雷?评论区聊聊,咱一起避坑~

上一篇:Java轻量级状态机在支付流程中的设计与实现 下一篇:叫你别乱封装,你看出事了吧

相关文章

相关应用

最近更新