分布式事务解决方案:最佳实践(面向零基础初学者的实战教程)

代码写到发光
2025-06-20 03:08
阅读 498

🧩 一、开篇:什么是分布式事务?它能做什么?

🧩 一、开篇:什么是分布式事务?它能做什么?

在我们开发软件系统时,尤其是大型互联网应用,数据往往分散在多个服务中。比如一个电商系统的订单服务和库存服务可能是两个独立的微服务,它们各自有自己的数据库。这个时候,当我们需要进行下单操作时,就需要同时减少库存新增订单,这两个操作必须同时成功或同时失败,否则就会出现数据不一致的问题。

这就是我们今天要讲的核心问题 —— 分布式事务

分布式事务就是在多个服务(通常是远程调用)之间保持事务的一致性。

通俗点说就是:跨多个“地盘”的事情,要么一起成功,要么一起回滚!

✨ 常见的使用场景:

  • 电商平台下单(库存 + 订单 + 支付)
  • 银行转账(从A账户转到B账户)
  • 跨公司业务协作

接下来你将亲手体验一个完整的实战项目,并学会如何解决这个常见但棘手的问题!


⚙️ 二、环境准备:搭建我们的实验平台

⚙️ 二、环境准备:搭建我们的实验平台

为方便学习,我们将使用以下几个技术组件:

技术 版本 说明
Spring Boot 2.7.x 快速构建Java后端服务
Nacos 2.1.x 作为注册中心和服务配置中心
Seata 1.6.x 开源的分布式事务框架
MySQL 5.7+ 存储数据

步骤一:安装 Java 和 Maven

如果你没有安装 Java,请前往 Oracle官网 下载 JDK。

然后安装 Maven,用于管理依赖库:

brew install maven # macOS 用户
sudo apt-get install maven # Ubuntu/Linux 用户

步骤二:下载并启动 Nacos

Nacos 是阿里巴巴开源的服务发现与配置管理平台。

  1. 下载 Nacos(选择 release 包):

    wget https://github.com/alibaba/nacos/releases/download/v2.1.2/nacos-server-2.1.2.zip
    
  2. 解压并运行:

    unzip nacos-server-2.1.2.zip
    cd nacos/bin
    startup.sh -m standalone # 单机模式启动
    
  3. 访问管理界面:http://localhost:8848/nacos
    默认账号密码都是 nacos

步骤三:下载并启动 Seata

Seata 是我们解决分布式事务的关键工具。

  1. 下载 Seata Server:

    wget https://github.com/seata/seata/releases/download/v1.6.1/seata-server-1.6.1.zip
    
  2. 解压并启动:

    unzip seata-server-1.6.1.zip
    cd seata/bin
    seata-server.bat -p 8091 -n 1 # Windows
    ./seata-server.sh -p 8091 -n 1 # Linux/Mac
    

此时,我们的服务调度中心已经准备好了!


🔍 三、核心概念:这些术语到底啥意思?

🔍 三、核心概念:这些术语到底啥意思?

为了让你更容易理解,我来用生活中的例子解释几个重要概念。

🧱 1. 分布式系统 vs 单体系统

  • 单体系统:就像一家人住在一套房子,厨房、卧室都在一起。
  • 分布式系统:像一家人搬去不同城市住,沟通就要靠电话、微信了。

💬 2. 什么是事务(Transaction)?

想象你在银行取钱:

  • 打卡 → 检查余额 → 出钞 → 更新余额

这四个动作必须都成功才算一次完整的操作。一旦中间出错,整个流程要撤回来,不能只扣钱不出钞。

这个就叫事务。它有四大特性:

ACID(酸碱平衡法,记住就好 😄)

缩写 含义 示例
A(原子性) 全部成功或全部失败 取钱只能全扣款 + 出钱
C(一致性) 数据前后状态保持合法 不会因取钱导致余额负数
I(隔离性) 多个事务互不影响 你取钱和别人转账是分开处理的
D(持久性) 成功的数据不会丢 取完钱断电了也不能抹掉记录

🌐 3. CAP理论简介(选学)

CAP 理论告诉我们:在一个分布式系统中,一致性(C)、可用性(A)、分区容忍性(P) 最多只能同时满足其中两个。

Seata 主要是在 CAP 中选择了 CP(一致性 + 分区容忍),优先保证数据正确。

🔄 4. Seata 的几个关键角色

角色 含义 类比
TC(事务协调者) 负责事务开始和提交 法院法官
TM(事务发起者) 定义全局事务边界 总经理
RM(资源管理者) 控制本地事务执行 小组组长

现在我们知道 Seata 是怎么工作的了。那我们接着来实战!


💻 四、实战项目:实现一个简单的下单 + 扣库存功能

我们将搭建两个服务:

  • 订单服务 order-service
  • 库存服务 storage-service

我们要实现:

用户下单的时候库存自动减少。如果某个步骤出错,所有更改都要恢复回去!


第一步:创建 Spring Boot 项目

你可以用 Spring Initializr 创建两个项目:

  • Order Service
  • Storage Service

添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

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

第二步:编写数据库表结构

分别在两个库中创建表:

库存服务(storage)

CREATE TABLE `storage` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `product_id` BIGINT DEFAULT NULL,
  `used` INT DEFAULT 0,
  `residue` INT DEFAULT 0,
  PRIMARY KEY (`id`)
);

订单服务(order)

CREATE TABLE `orders` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `user_id` BIGINT DEFAULT NULL,
  `product_id` BIGINT DEFAULT NULL,
  `count` INT DEFAULT 0,
  `status` VARCHAR(255),
  PRIMARY KEY (`id`)
);

记得初始化几条测试数据。


第三步:引入 Seata 配置(order-service 和 storage-service)

在 application.yml 文件中加上:

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_tx_group
feign:
  client:
    config:
      default:
        http-replace:
          enabled: true

同时,在 resources 目录下新建文件 file.conf

transport {
  type = "TCP"
}
client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 30
    retry.times = 30
  }
}

别忘了加一个 Seata 的启动监听器类:

import io.seata.config.ConfigurationFactory;
import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoDataSourceProxy
public class SeataConfig {
}

第四步:在 order-service 发起分布式事务

我们在订单服务中通过 FeignClient 调用库存服务:

@FeignClient(name = "storage-service")
public interface StorageServiceClient {

    @PostMapping("/deduct")
    void deductStock(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

然后编写下单方法(带分布式事务注解):

@Service
public class OrderService {

    @Resource
    private OrderRepository orderRepo;

    @Resource
    private StorageServiceClient storageService;

    @GlobalTransactional(name = "create_order_with_stock", rollbackFor = Exception.class)
    public void createOrder(Long userId, Long productId, int count) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(userId);
        order.setProductId(productId);
        order.setCount(count);
        order.setStatus("CREATED");
        orderRepo.save(order);

        // 2. 调用库存服务扣减库存
        try {
            storageService.deductStock(productId, count);
        } catch (Exception e) {
            throw new RuntimeException("库存扣除失败!", e);
        }
    }
}

⚠️ 注意:只要抛出异常,Seata 会自动帮你回滚!


第五步:在 storage-service 编写扣库存逻辑

@RestController
@RequestMapping("/stock")
public class StorageController {

    @Resource
    private StorageRepository storageRepo;

    @PostMapping("/deduct")
    public void deductStock(@RequestParam Long productId, @RequestParam Integer count) {
        Storage storage = storageRepo.findByProductId(productId);

        if (storage.getResidue() < count) {
            throw new RuntimeException("库存不足!");
        }

        storage.setUsed(storage.getUsed() + count);
        storage.setResidue(storage.getResidue() - count);
        storageRepo.save(storage);
    }
}

第六步:测试一下!

  1. 启动 Nacos
  2. 启动 Seata
  3. 启动 order-service 和 storage-service
  4. 使用 Postman 或 curl 请求 /order/create 接口

示例请求体(POST):

{
  "userId": 1001,
  "productId": 1001,
  "count": 2
}

查看数据库是否两个表都有变化?再试一个错误的请求(比如库存不够),看是否会自动回滚!


❓五、新手常问问题解答

Q1:为什么我启动报错了?

答:有可能是 Nacos 或 Seata 没启动,或者配置没写对。检查端口号是否被占用,日志是否有报错信息。

Q2:@GlobalTransactional 注解失效?

答:请确保你用了 Seata Starter,并且数据库支持代理。此外,方法签名必须用 public,并且不能是静态方法。

Q3:我用的是 Redis 或 MongoDB,能用 Seata 吗?

答:Seata 目前主要支持关系型数据库(如 MySQL、Oracle)。NoSQL 有一些适配器但不推荐初级阶段尝试。

Q4:还有哪些替代方案?

答:常见的方案还有:

  • TCC(两阶段补偿事务)
  • Saga(长事务)
  • 消息队列 + 本地事务表(进阶内容)

Seata 是目前最简单上手的之一。


🚀 六、下一步学习建议

你现在掌握了使用 Seata 实现分布式事务的基本能力,接下来可以沿着这条路继续进阶:

✅ 学习路线图:

  1. 掌握 Seata 高级用法
    • AT 模式 vs TCC 模式
    • 分布式锁与重试策略
  2. 学习分布式事务其他方案
    • TCC(Try-Cancel-Confirm)
    • Saga 模式
    • 消息事务(RabbitMQ / Kafka + RocketMQ 事务消息)
  3. 研究底层原理
    • 两阶段提交协议(2PC)
    • 三阶段提交协议(3PC)
    • Paxos、Raft 算法简介
  4. 实践更复杂的项目
    • 整合 Spring Cloud Alibaba 生态体系
    • 使用 SkyWalking 进行分布式追踪
    • 使用 Sentinel 进行流量控制

🎉 结语:你已经迈出了第一步!

这篇文章帮助你从零基础了解什么是分布式事务,并通过实际代码演示了 Seata 的基本用法。虽然只是起步,但这已经是大多数程序员的第一大难关!

希望你能坚持下去,动手敲一遍代码、改一点Bug、跑一下接口,这样印象才会更深。分布式系统之路很远,但每一步都很精彩!


📌 课后作业:你可以尝试加入支付服务,让整个“下单 + 扣库存 + 扣余额”成为一个整体事务。

如有疑问,欢迎留言提问 👇


字数统计:约3788字
图文结构化展示,循序渐进讲解,适合零基础入门。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝