DEV Community

Woody
Woody

Posted on

分布式事务 Distributed Transaction

在 Java 开发中,分布式事务是指跨越多个数据库、服务或微服务节点的事务操作,这些操作要么全部成功,要么全部失败,保持一致性(ACID原则的一致性和原子性),就像本地事务一样。


🔹一、为什么需要分布式事务?

当系统从单体架构演化为微服务架构分布式架构,一个业务操作可能涉及:

  • 多个数据库(如订单库、库存库)
  • 多个微服务(如订单服务、支付服务、物流服务)
  • 多种存储系统(如关系型数据库 + 消息队列)

这时一个操作无法用传统的本地事务(例如 JDBC 事务)来保证整体一致性,因此需要分布式事务机制。


🔹二、常见的分布式事务场景

业务场景 涉及系统
下单流程 订单服务、库存服务、账户服务
支付成功后发货 支付系统、物流系统
微服务调用链 A服务调用B,B调用C,事务需统一回滚

🔹三、解决方案类型

1. 两阶段提交(2PC,XA协议)

✅ 特点:

  • 保证强一致性
  • 使用数据库的 XA 事务支持(如 MySQL InnoDB + Atomikos)

❌ 缺点:

  • 性能差、资源锁定严重
  • 数据库耦合度高,不适合高并发业务场景

2. 本地消息表 + 事务消息(可靠消息最终一致性)

如阿里巴巴提出的 TCC / 消息驱动 模式:

示例流程:

  1. 业务服务写入本地消息表 + 本地数据库事务提交(同一个本地事务)
  2. 消息服务异步投递 MQ 消息
  3. 下游服务接收消息并执行操作

✅ 优点:

  • 高性能
  • 最终一致性

✅ 中间件代表:

  • RocketMQ 的事务消息
  • Kafka 的 exactly-once
  • RabbitMQ 手动 ack + 重试机制

3. TCC 模型(Try-Confirm-Cancel)

每个服务实现三个接口:

  • Try: 预留资源
  • Confirm: 确认操作
  • Cancel: 回滚操作

适合业务粒度明确的操作,例如:支付扣款、库存锁定

✅ 优点:

  • 性能好,适合强一致性要求
  • 控制力强,可按业务自定义补偿

❌ 缺点:

  • 开发复杂,服务需实现三段逻辑

4. SAGA 模型(长事务 + 补偿)

Saga 是由一系列本地事务组成,每个本地事务执行后,若失败则依次执行补偿事务进行回滚。

适用场景:

  • 长事务、订单流程(下单、锁库存、扣余额)

常用工具:

  • Apache ServiceComb Saga
  • Netflix Conductor
  • Seata 的 Saga 模式

🔹四、分布式事务中间件推荐(Java 生态)

中间件 特点
Seata (阿里) 支持 AT / TCC / SAGA / XA 模式
LCN(Jdchain) 支持基于数据库的分布式事务
Hmily 高性能的 TCC 实现
Atomikos 老牌 Java 分布式事务管理器,支持 XA
RocketMQ事务消息 基于 MQ 的事务一致性
Canal + 消息队列 异步最终一致性方案

🔹五、总结:不同事务模型对比

模型 一致性 性能 应用复杂度 场景
XA(2PC) 强一致 简单 银行、转账等高要求一致性
TCC 强一致 支付、扣库存、下单等
SAGA 最终一致 中等 电商业务流程
可靠消息 最终一致 中等 微服务调用链

如果你是面试时被问:

❓面试官:如何保证分布式事务一致性?

你可以答:

分布式事务的一致性可以通过 Seata、TCC 模式或本地消息表 + MQ 实现。在业务允许最终一致性的情况下,我们通常采用异步消息 +幂等 + 重试机制提高性能和可用性;若业务要求强一致,可选择 TCC 或 XA 协议。


Seata 是阿里开源的 分布式事务解决方案,支持 AT、TCC、SAGA 和 XA 四种事务模式,尤其适合 Spring Cloud / Spring Boot / Dubbo / Nacos 等微服务环境。


✅ 一、Seata 使用场景(以 AT 模式为例)

假设有一个电商系统,下单流程需要跨越以下 3 个微服务:

  • 订单服务:写入订单表
  • 库存服务:扣减库存
  • 账户服务:扣减用户余额

我们希望这三步要么全部成功,要么全部失败,Seata 就能帮我们实现这个分布式事务一致性


🧰 二、Seata 快速使用(Spring Boot + MySQL 示例)

1️⃣ 环境准备

  • JDK 8+
  • Spring Boot 项目(多个微服务模块)
  • 数据库:MySQL 5.7+(需支持 undo log)
  • 注册中心(如:Nacos)
  • 下载并启动 Seata Server:https://github.com/seata/seata/releases

2️⃣ 数据库改造(重要)

每个微服务的业务表必须满足:

  • 表必须有主键
  • 每个库都加 undo_log 表(用于事务回滚)
CREATE TABLE `undo_log` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `branch_id` BIGINT(20) NOT NULL,
  `xid` VARCHAR(100) NOT NULL,
  `context` VARCHAR(128) NOT NULL,
  `rollback_info` LONGBLOB NOT NULL,
  `log_status` INT(11) NOT NULL,
  `log_created` DATETIME NOT NULL,
  `log_modified` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB;
Enter fullscreen mode Exit fullscreen mode

3️⃣ 引入依赖(示例:Spring Boot)

<!-- seata 依赖 -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.7.1</version> <!-- 推荐和 seata-server 同版本 -->
</dependency>
Enter fullscreen mode Exit fullscreen mode

4️⃣ 配置 application.yml(微服务)

spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    url: jdbc:mysql://localhost:3306/order_db
    username: root
    password: root

seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
    grouplist:
      default: 127.0.0.1:8091
  data-source-proxy-mode: AT
Enter fullscreen mode Exit fullscreen mode

注意:

  • tx-service-group 是事务组名,用于注册全局事务协调器
  • vgroup-mapping 是事务组和 Seata Server 实例的映射

5️⃣ 编写代码

在业务入口标注 @GlobalTransactional

@RestController
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @PostMapping("/order/create")
    @GlobalTransactional(name = "create-order-tx", rollbackFor = Exception.class)
    public String createOrder(@RequestBody OrderDto dto) {
        orderService.createOrder(dto);
        return "success";
    }
}
Enter fullscreen mode Exit fullscreen mode

各个服务方法使用本地事务(@Transactional

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private InventoryClient inventoryClient;

    @Transactional
    public void createOrder(OrderDto dto) {
        orderMapper.insert(dto.toEntity());
        inventoryClient.decreaseStock(dto.getProductId(), dto.getAmount());
        // throw new RuntimeException("模拟异常"); // 会全局回滚
    }
}
Enter fullscreen mode Exit fullscreen mode

Seata 会通过代理数据源和 undo_log 自动实现本地事务的回滚


✅ 三、关键点总结

步骤 必做事项
🧱 数据库 所有表都必须有主键,并加 undo_log
⚙ 配置 每个服务都要配置 Seata 的 group、注册中心等
💾 数据源代理 Seata 会自动代理 Druid / HikariCP 数据源
🔁 全局事务注解 @GlobalTransactional 只能加在入口方法
📦 Seata Server 需要启动并注册到 Nacos(或其他)

✅ 四、常见问题排查

问题 原因
事务不生效 忘记使用 @GlobalTransactional
回滚失败 表无主键或无 undo_log
数据未代理 未启用 seata 的数据源代理模式
服务不注册 Nacos 未配置 / group 映射未设置

✅ 五、进阶推荐

  • 模式切换:Seata 支持 AT / TCC / SAGA 等,可灵活选择
  • 与 Dubbo、Feign、RocketMQ 整合
  • 二阶段事务失败补偿机制
  • 分布式锁 + 幂等性控制配套

Top comments (0)