欢迎光临
我们一直在努力

深入剖析 Seata:分布式事务的终极解决方案

第一章:Seata 概况介绍

一、前言

Seata(Simple Extensible Autonomous Transaction Architecture) 是蚂蚁集团与阿里巴巴联合开源的分布式事务解决方案,旨在解决微服务架构下的跨服务、跨数据库的事务一致性问题。它通过 AT、TCC、Saga、XA 四种模式,为不同业务场景提供灵活的事务处理方案。

核心定位

1、全场景覆盖
从数据库水平拆分、服务化拆分,到最终一致性,Seata提供全场景的分布式事务解决方案。

2、零侵入性
AT模式无需修改业务代码,实现“业务无感知”的事务管理。

3、社区活跃
开源半年即获得1.1万+ star,持续迭代优化。

二、核心组件与协作机制

TC(Transaction Coordinator)

作为独立服务端部署,负责维护全局事务状态,协调并驱动全局事务的提交或回滚。

TM(Transaction Manager)

定义全局事务边界,负责开启、提交或回滚全局事务。

RM(Resource Manager)

管理分支事务,向TC注册分支事务并上报状态,执行本地事务的提交/回滚。

Seata 分布式事务协作流程:

三、四种事务模式深度解析

1、AT 模式(自动事务)

原理 :通过数据源代理自动生成SQL快照和回滚日志,实现无侵入式事务管理。
优点 :零侵入性,开发简单。
缺点 :存在全局锁竞争,可能影响性能。

2、TCC 模式(Try-Confirm-Cancel)

原理 :业务方需实现Try(预留资源)、Confirm(确认执行)、Cancel(取消释放)三个接口。
优点 :高性能,无全局锁。
缺点 :业务侵入性强,需实现补偿逻辑。

3、Saga 模式

原理 :将事务拆分为多个本地事务,每个本地事务都有对应的补偿操作。
优点 :适用于长事务流程,异步执行提高吞吐。
缺点 :不保证隔离性,需处理脏写问题。

4、XA 模式

原理 :基于数据库XA协议实现两阶段提交。
优点 :强一致性,符合ACID。
缺点 :性能开销大,资源占用高。

第二章:Seata AT模式:分布式事务的无侵入之道

Seata AT模式通过“自动反向补偿”机制,在业务无感知的情况下实现跨服务事务一致性。它像一位隐形的协调者,在幕后记录数据变更的“快照”,确保分布式系统在异常时能精准回滚。

核心在于:一阶段提交业务数据与回滚日志,二阶段异步删除或补偿,完美平衡了开发效率与数据可靠性

一、AT模式核心原理剖析

AT(Automatic Transaction)模式的精髓在于“自动反向补偿”。它不像传统2PC那样在二阶段才决定提交,而是在一阶段就提交本地事务,同时生成用于回滚的Undo Log。

🎯 两阶段执行流程

第一阶段:本地提交

1. 解析SQL:DataSourceProxy拦截业务SQL,自动解析语义
2. 查询前镜像:执行SELECT … FOR UPDATE获取修改前数据
3. 执行业务SQL:正常执行UPDATE/INSERT/DELETE
4. 查询后镜像:获取修改后的数据状态
5. 生成Undo Log:将前后镜像序列化存入undo_log表
6. 提交本地事务:业务数据和Undo Log在同一个事务中提交

第二阶段:全局协调

• 全局提交:TC收到所有分支成功报告,异步删除各分支的Undo Log
• 全局回滚:TC通知各RM,根据Undo Log执行反向SQL补偿
• 关键设计:回滚SQL会带上WHERE条件校验,防止脏写

二、事务隔离与全局锁机制

AT模式默认工作在读未提交隔离级别,通过全局锁机制防止脏写,但允许读到全局事务的中间状态。

🔐 全局锁的实现原理

1、锁注册时机

分支事务注册时,RM会将修改行的主键作为LockKey上报给TC。TC维护全局锁表,确保同一行数据在同一时刻只被一个全局事务修改。

2、锁竞争处理

当多个全局事务竞争同一行锁时,后发起的事务会进入重试机制。通过@GlobalLock注解+SELECT FOR UPDATE可实现全局读已提交,防止脏读。

3、注解区别

• @GlobalTransactional:开启完整全局事务,较重
• @GlobalLock:仅检查全局锁,不开启全局事务,轻量级

三、SpringBoot集成实战代码

以下是一个完整的订单-库存分布式事务示例,展示如何在SpringBoot中集成Seata AT模式。

Maven依赖配置
<!– SpringBoot基础依赖 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!– Seata客户端 –>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>

<!– 数据库相关 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

//application.yml配置
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://localhost:3306/seata_demo?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource

cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group # 事务组名称,需与Seata Server配置一致

seata:
enabled: true
application-id: spring.application.nametx−service−group:{spring.cloud.alibaba.seata.tx-service-group}
# Seata Server配置
service:
vgroup-mapping:
my_test_tx_group: default # 事务组映射到默认集群
grouplist:
default: 127.0.0.1:8091 # TC服务器地址
config:
type: file # 配置文件类型
registry:
type: file # 注册中心类型

数据库表结构(需在每个参与库中创建)

— 业务表:订单表
CREATE TABLE `order` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
`product_id` BIGINT(20) NOT NULL COMMENT '商品ID',
`count` INT(11) NOT NULL COMMENT '购买数量',
`amount` DECIMAL(10,2) NOT NULL COMMENT '订单金额',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

— 业务表:库存表
CREATE TABLE `stock` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`product_id` BIGINT(20) NOT NULL COMMENT '商品ID',
`total` INT(11) NOT NULL COMMENT '总库存',
`used` INT(11) NOT NULL DEFAULT 0 COMMENT '已用库存',
`residue` INT(11) NOT NULL COMMENT '剩余库存',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

— Seata AT模式必需表:undo_log(每个参与库都需要)
CREATE TABLE `undo_log` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`branch_id` BIGINT(20) NOT NULL COMMENT '分支事务ID',
`xid` VARCHAR(100) NOT NULL COMMENT '全局事务ID',
`context` VARCHAR(128) NOT NULL COMMENT '上下文',
`rollback_info` LONGBLOB NOT NULL COMMENT '回滚信息',
`log_status` INT(11) NOT NULL COMMENT '状态',
`log_created` DATETIME NOT NULL COMMENT '创建时间',
`log_modified` DATETIME NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Service层:全局事务业务逻辑

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

@Autowired
private OrderDao orderDao;

@Autowired
private StockDao stockDao;

/**
* 创建订单 – 分布式事务入口
* @GlobalTransactional 注解开启全局事务
* rollbackFor = Exception.class 表示所有异常都触发回滚
* timeoutMills = 60000 全局事务超时时间60秒
*/
@GlobalTransactional(
name = "createOrderTx", // 全局事务名称
rollbackFor = Exception.class,
timeoutMills = 60000
)
@Transactional(rollbackFor = Exception.class) // 本地事务注解
public void createOrder(OrderDTO orderDTO) {
// 1. 参数校验
if (orderDTO.getCount() <= 0) {
throw new IllegalArgumentException("购买数量必须大于0");
}

// 2. 扣减库存(分支事务1)
// 这里会生成undo_log,记录库存修改前的镜像
boolean success = stockDao.decreaseStock(
orderDTO.getProductId(),
orderDTO.getCount()
);

if (!success) {
throw new RuntimeException("库存不足,扣减失败");
}

// 3. 创建订单(分支事务2)
// 这里也会生成undo_log,记录订单插入前的状态(null)
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setProductId(orderDTO.getProductId());
order.setCount(orderDTO.getCount());
order.setAmount(orderDTO.getAmount());

orderDao.createOrder(order);

// 4. 模拟异常场景(测试回滚)
// 取消注释以下代码可以测试分布式事务回滚
// if (orderDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
// throw new RuntimeException("金额异常,触发全局回滚");
// }

// 5. 记录日志(非事务操作)
log.info("订单创建成功,订单ID: {}", order.getId());
}

/**
* 仅检查全局锁,不开启全局事务
* 适用于需要防止脏读但不需事务管理的查询
*/
@GlobalLock // 轻量级全局锁检查
public Stock getStockWithLock(Long productId) {
return stockDao.selectForUpdate(productId);
}
}

DAO层:数据访问实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

@Repository
public class OrderDao {

@Autowired
private JdbcTemplate jdbcTemplate;

/**
* 创建订单
* 注意:这里使用的是Spring的JdbcTemplate
* Seata的DataSourceProxy会自动代理数据源,无需特殊处理
*/
public Long createOrder(Order order) {
final String sql = "INSERT INTO `order` " +
"(user_id, product_id, count, amount) " +
"VALUES (?, ?, ?, ?)";

KeyHolder keyHolder = new GeneratedKeyHolder();

jdbcTemplate.update(connection -> {
// 创建PreparedStatement,指定返回主键
PreparedStatement ps = connection.prepareStatement(
sql,
Statement.RETURN_GENERATED_KEYS
);
ps.setLong(1, order.getUserId());
ps.setLong(2, order.getProductId());
ps.setInt(3, order.getCount());
ps.setBigDecimal(4, order.getAmount());
return ps;
}, keyHolder);

// 返回生成的主键ID
return keyHolder.getKey().longValue();
}
}

@Repository
public class StockDao {

@Autowired
private JdbcTemplate jdbcTemplate;

/**
* 扣减库存
* 使用乐观锁防止超卖,通过residue字段做条件判断
* Seata会为这个UPDATE自动生成undo_log
*/
public boolean decreaseStock(Long productId, Integer count) {
String sql = "UPDATE stock SET " +
"used = used + ?, " +
"residue = residue – ? " +
"WHERE product_id = ? AND residue >= ?";

// 执行更新,返回影响行数
int affectedRows = jdbcTemplate.update(
sql,
count, count, productId, count
);

// 影响行数大于0表示扣减成功
return affectedRows > 0;
}

/**
* 查询库存(带行锁)
* 配合@GlobalLock使用,实现全局读已提交
*/
public Stock selectForUpdate(Long productId) {
String sql = "SELECT * FROM stock WHERE product_id = ? FOR UPDATE";

return jdbcTemplate.queryForObject(
sql,
(rs, rowNum) -> {
Stock stock = new Stock();
stock.setId(rs.getLong("id"));
stock.setProductId(rs.getLong("product_id"));
stock.setTotal(rs.getInt("total"));
stock.setUsed(rs.getInt("used"));
stock.setResidue(rs.getInt("residue"));
return stock;
},
productId
);
}
}

Controller层:REST接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/order")
public class OrderController {

@Autowired
private OrderService orderService;

/**
* 创建订单接口
* POST /api/order/create
* Content-Type: application/json
*/
@PostMapping("/create")
public ApiResponse createOrder(@RequestBody OrderDTO orderDTO) {
try {
// 调用Service层,开启全局事务
orderService.createOrder(orderDTO);

return ApiResponse.success("订单创建成功");

} catch (IllegalArgumentException e) {
// 参数校验异常,返回400
return ApiResponse.fail(400, e.getMessage());

} catch (RuntimeException e) {
// 业务异常,返回500
// 这里异常会触发@GlobalTransactional回滚
log.error("订单创建失败: {}", e.getMessage(), e);
return ApiResponse.fail(500, "订单创建失败: " + e.getMessage());

} catch (Exception e) {
// 其他异常
log.error("系统异常: {}", e.getMessage(), e);
return ApiResponse.fail(500, "系统繁忙,请稍后重试");
}
}

/**
* 查询库存(带全局锁检查)
* GET /api/stock/{productId}
*/
@GetMapping("/stock/{productId}")
public ApiResponse getStock(@PathVariable Long productId) {
try {
// 使用@GlobalLock确保读到已提交的数据
Stock stock = orderService.getStockWithLock(productId);
return ApiResponse.success(stock);

} catch (Exception e) {
log.error("查询库存失败: {}", e.getMessage(), e);
return ApiResponse.fail(500, "查询失败");
}
}
}

// DTO对象
@Data
public class OrderDTO {
private Long userId;
private Long productId;
private Integer count;
private BigDecimal amount;
}

// 统一响应对象
@Data
public class ApiResponse {
private Integer code;
private String message;
private Object data;

public static ApiResponse success(Object data) {
ApiResponse response = new ApiResponse();
response.setCode(200);
response.setMessage("success");
response.setData(data);
return response;
}

public static ApiResponse fail(Integer code, String message) {
ApiResponse response = new ApiResponse();
response.setCode(code);
response.setMessage(message);
return response;
}
}

四、注意事项与最佳实践

🔴 不支持的SQL类型

• 包含NOW()、RAND()等非确定性函数的UPDATE
• 多表JOIN的复杂UPDATE/DELETE
• 影响行数不确定的批量操作
• 存储过程、触发器执行的SQL

🟡 性能优化建议

• 避免单行数据过大,Undo Log会记录完整行数据
• 热点数据考虑使用TCC模式替代
• 合理设置全局事务超时时间,默认60秒
• 使用@GlobalLock替代不必要的@GlobalTransactional

🟢 部署配置要点

1. TC集群高可用:生产环境至少部署3节点
2. 数据库配置:隔离级别需为READ COMMITTED以上
3. undo_log表:每个参与库都必须创建
4. 网络超时:合理配置client.rm.report.retry.count等参数

五、扩展思考:AT模式的适用边界

AT模式通过SQL解析和自动补偿实现了“无侵入”的优雅,但这种自动化也带来了限制。它最适合简单的CRUD操作,当遇到复杂业务逻辑时,可能需要考虑其他方案。

场景AT模式替代方案
简单主键UPDATE ✅ 完美适配
批量更新/复杂JOIN ❌ 不支持 TCC模式
高并发热点数据 ⚠️ 性能差 本地消息表
非关系型数据库 ❌ 不支持 Saga模式
最终一致性即可 ✅ 可用 最大努力通知

第三章:TCC模式:分布式事务的柔性解决方案

在微服务架构中,TCC模式通过“业务补偿”机制解决跨服务事务一致性问题。它将一个分布式事务拆分为Try、Confirm、Cancel三个阶段,让开发者能够精细控制每个服务的资源操作,实现高性能的最终一致性。

一、核心三阶段流程

1. Try(尝试)

检查业务资源并预留,如冻结库存、预扣金额。此阶段只做资源锁定,不执行最终操作。

2. Confirm(确认)

所有Try成功后执行,真正提交业务操作,如实际扣款、确认库存。此操作必须保证幂等性。

3. Cancel(取消)

任一Try失败时执行,释放预留的资源,如解冻库存、返还金额。同样需要保证幂等性。

二、Seata TCC 实现机制

Seata为TCC模式提供了完整的框架支持,通过注解和代理机制简化了开发复杂度。

📝 核心注解与接口定义

@LocalTCC // 标记为TCC接口
public interface OrderTccService {

@TwoPhaseBusinessAction(
name = "prepareCreateOrder", // 全局唯一资源ID
commitMethod = "commit", // Confirm方法名
rollbackMethod = "rollback" // Cancel方法名
)
boolean prepareCreateOrder(
BusinessActionContext context,
@BusinessActionContextParameter(paramName = "orderNo") String orderNo,
Long userId, Long productId, Integer amount, BigDecimal money
);

boolean commit(BusinessActionContext context); // Confirm阶段
boolean rollback(BusinessActionContext context); // Cancel阶段
}

在实现类中,需要具体编写三个阶段的方法逻辑。以电商下单为例,Try阶段冻结库存并创建待确认订单,Confirm阶段确认订单并扣减库存,Cancel阶段释放冻结资源。

三、三大异常处理策略

TCC模式在实际应用中需要处理空回滚、幂等、悬挂三大异常场景,Seata提供了相应的解决方案。

1、空回滚(Empty Rollback)

Try未执行但Cancel被调用。Seata通过TCC事务控制表记录Try执行状态,Cancel时检查记录是否存在,不存在则判定为空回滚直接返回成功。

2、幂等(Idempotence)

Confirm/Cancel被重复调用。在事务控制表中增加状态字段(tried:1, committed:2, rollbacked:3),执行二阶段前检查状态,已执行则直接返回。

3、悬挂(Hanging)

Cancel先于Try执行导致资源永远锁定。在事务控制表增加suspended:4状态,Cancel时插入该状态记录,Try执行时检查到suspended状态则阻止执行。

四、Seata TCC 架构解析

Seata TCC遵循TC、TM、RM三角色模型,通过资源解析、资源管理、事务处理三个核心模块实现分布式事务协调。

组件角色在TCC中的职责
Transaction Coordinator (TC) 事务协调器 维护全局事务状态,驱动Confirm/Cancel回调
Transaction Manager (TM) 事务管理器 定义全局事务边界,开启/提交/回滚全局事务
Resource Manager (RM) 资源管理器 管理TCC资源,执行分支事务注册与上报

资源解析阶段,Seata通过GlobalTransactionScanner扫描带@TwoPhaseBusinessAction注解的TCC接口,解析为TCCResource并注册到TC。事务处理阶段,TccActionInterceptor代理拦截TCC调用,在Try执行前向TC注册分支事务。

📊 TCC vs AT 模式对比

Seata提供AT和TCC两种主要的事务模式,适用于不同业务场景。

  • 侵入性 :AT模式零侵入,自动生成undo log;TCC需要业务实现三个方法,侵入性强。
  • 性能:AT模式一阶段立即提交,二阶段异步清理,性能较高;TCC需要业务编码控制,性能依赖实现。
  • 适用场景:AT适用于数据库操作;TCC支持跨数据库、跨应用、非关系型数据源。
  • 隔离性:AT通过全局锁实现隔离;TCC依赖业务设计的资源预留机制。

五、实战建议与最佳实践

在实际项目中应用TCC模式时,需要注意以下关键点以确保系统的可靠性和稳定性。

🔐 幂等性设计

1、状态标记法

在事务控制表中记录执行状态,Confirm/Cancel前检查状态避免重复执行。

2、唯一键约束

使用全局事务ID+分支ID作为唯一键,数据库层面防止重复提交。

3、业务逻辑幂等

设计Confirm/Cancel业务逻辑时确保多次执行结果一致。

第四章:Seata Saga模式:长流程事务的柔性补偿之道

Saga模式是Seata分布式事务解决方案中专门为处理“长流程、多服务”的业务场景而设计的柔性事务方案。它通过将一个大事务拆分为一系列可补偿的本地小事务,在保证最终一致性的同时,避免了长时间的资源锁定,特别适合金融、电商等复杂业务流程。

与AT模式的自动回滚和TCC模式的三阶段预留不同,Saga采用“正向操作+逆向补偿”的机制,每个参与者只需实现业务操作和对应的补偿操作,由状态机引擎驱动整个流程的执行与回滚。

一、核心原理:事件驱动的状态机

Saga模式的核心在于“状态机引擎”。它将一个完整的业务流程定义为一个状态图,每个状态代表一个服务调用节点。引擎基于事件驱动架构,异步执行各个状态,并记录完整的执行日志,确保在异常发生时能够按定义好的路径进行回滚补偿。

⚙️ Saga事务执行流程

1、流程编排

通过JSON状态语言定义业务流程,明确每个服务的执行顺序和对应的补偿服务。

2、正向执行

状态机引擎按顺序调用各个服务的正向操作,每个操作完成后立即提交本地事务。

3、异常回滚

当任一正向操作失败时,引擎反向执行已成功节点的补偿操作,进行业务回滚。

4、最终一致

通过补偿机制,系统最终达到一致状态,但不保证中间过程的隔离性。

二、模式对比:Saga的适用场景

事务模式核心机制性能特点适用场景
AT模式 基于SQL解析自动生成回滚日志 中等,有全局行锁 对业务无侵入的通用场景
TCC模式 Try-Confirm-Cancel三阶段预留 高,无锁设计 对性能要求高的核心系统
Saga模式 正向操作+逆向补偿 高,一阶段即提交 长流程、最终一致的业务系统

三、Spring Boot集成实战

下面以经典的“创建订单-扣减库存”场景为例,演示如何在Spring Boot项目中集成Seata Saga模式。我们将创建两个服务:订单服务(OrderService)和库存服务(StockService)。

1. 环境准备与依赖配置

首先在项目的pom.xml中添加Seata Spring Boot Starter依赖

2、在application.yml中配置Seata客户端,指向已部署的Seata Server(TC):

seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group # 事务组名称,需与Server配置一致
config:
type: nacos # 配置中心类型,支持nacos、apollo等
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
registry:
type: nacos # 注册中心类型
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP

3. 定义业务服务与补偿方法

订单服务和库存服务需要分别实现正向业务方法和对应的补偿方法。补偿方法用于在事务失败时回滚已提交的操作。

// OrderService.java – 订单服务
@Service
public class OrderService {

@Autowired
private OrderMapper orderMapper;

/**
* 正向操作:创建订单
* @param order 订单实体
*/
@Transactional
public void createOrder(Order order) {
// 插入订单记录
orderMapper.insert(order);
// 这里可以添加其他业务逻辑
System.out.println("订单创建成功,订单ID:" + order.getId());
}

/**
* 补偿操作:取消订单(逆向回滚)
* @param orderId 订单ID
*/
@Transactional
public void cancelOrder(Long orderId) {
// 根据订单ID删除订单记录
orderMapper.deleteById(orderId);
System.out.println("订单补偿成功,已删除订单ID:" + orderId);
}
}

// StockService.java – 库存服务
@Service
public class StockService {

@Autowired
private StockMapper stockMapper;

/**
* 正向操作:扣减库存
* @param productId 商品ID
* @param quantity 扣减数量
*/
@Transactional
public void deductStock(Long productId, Integer quantity) {
Stock stock = stockMapper.selectById(productId);
if (stock.getQuantity() >= quantity) {
// 扣减库存
stock.setQuantity(stock.getQuantity() – quantity);
stockMapper.updateById(stock);
System.out.println("库存扣减成功,商品ID:" + productId + ",扣减数量:" + quantity);
} else {
throw new RuntimeException("库存不足,商品ID:" + productId);
}
}

/**
* 补偿操作:恢复库存(逆向回滚)
* @param productId 商品ID
* @param quantity 恢复数量
*/
@Transactional
public void restoreStock(Long productId, Integer quantity) {
Stock stock = stockMapper.selectById(productId);
// 恢复库存
stock.setQuantity(stock.getQuantity() + quantity);
stockMapper.updateById(stock);
System.out.println("库存补偿成功,商品ID:" + productId + ",恢复数量:" + quantity);
}
}

3. 定义Saga状态机(JSON配置)

在resources/statelang目录下创建order-stock-saga.json文件,定义业务流程的状态图:

{
"Name": "OrderStockSaga",
"Comment": "订单-库存Saga流程:先创建订单,再扣减库存",
"Version": "1.0.0",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService", // Spring Bean名称
"ServiceMethod": "createOrder", // 正向方法
"InputParameters": ["$.order"], // 输入参数,从上下文获取order对象
"CompensateState": "CancelOrder", // 补偿状态名称
"Next": "DeductStock" // 成功后执行的下一个状态
},
"CancelOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "cancelOrder", // 补偿方法
"InputParameters": ["$.order.id"] // 输入参数为订单ID
},
"DeductStock": {
"Type": "ServiceTask",
"ServiceName": "stockService",
"ServiceMethod": "deductStock", // 正向方法
"InputParameters": [".order.productId",".order.quantity"],
"CompensateState": "RestoreStock", // 补偿状态名称
"Next": "Succeed" // 成功后流程结束
},
"RestoreStock": {
"Type": "ServiceTask",
"ServiceName": "stockService",
"ServiceMethod": "restoreStock", // 补偿方法
"InputParameters": [".order.productId",".order.quantity"]
},
"Succeed": {
"Type": "Succeed" // 成功结束状态
},
"Fail": {
"Type": "Fail",
"ErrorCode": "ORDER_FAILED",
"Message": "订单创建流程失败"
}
}
}

4. 启动Saga业务流程

在Controller中注入状态机引擎,启动Saga分布式事务流程:

@RestController
@RequestMapping("/order")
public class OrderController {

@Autowired
private StateMachineEngine stateMachineEngine; // Seata Saga状态机引擎

/**
* 创建订单入口
* @param order 订单请求参数
* @return 流程执行状态
*/
@PostMapping("/create")
public String createOrder(@RequestBody Order order) {
// 1. 准备状态机执行参数
Map params = new HashMap<>();
params.put("order", order); // 将订单对象放入上下文

// 2. 生成业务键(用于幂等控制)
String businessKey = order.getId() != null ?
order.getId().toString() :
UUID.randomUUID().toString();

try {
// 3. 启动状态机实例
StateMachineInstance instance = stateMachineEngine.startWithBusinessKey(
"OrderStockSaga", // 状态机名称,对应JSON中的Name
null, // 租户ID,可为空
params, // 输入参数
businessKey // 业务键,保证幂等性
);

// 4. 返回执行状态
return "Saga流程启动成功,状态:" + instance.getStatus();

} catch (Exception e) {
// 5. 异常处理
return "Saga流程启动失败:" + e.getMessage();
}
}
}

四、服务设计关键原则

在Saga模式下设计服务时,必须遵循以下关键原则以确保系统的健壮性:

🔐 三大保障机制

允许空补偿

补偿服务被调用时,可能对应的正向操作并未执行(如网络超时)。此时补偿服务应返回成功,并记录业务主键,避免重复补偿。

防悬挂控制

当补偿服务比正向服务先执行时,正向服务应检查业务主键是否已在空补偿记录中存在。若存在,则拒绝执行,防止数据不一致。

幂等控制

网络超时可能导致重试,因此正向和补偿服务都必须保证幂等性。可通过事务ID或业务主键判重,确保多次调用结果一致。

五、最佳实践与注意事项

Saga模式虽然灵活强大,但在实际应用中需要注意以下几点:

  • 业务设计原则:遵循"宁可长款,不可短款"原则。在资金类业务中,先扣款再入账,即使发生脏写也不会造成资金损失。
  • 隔离性缺失:Saga不保证隔离性,可能出现脏读。业务上需要容忍中间状态,或通过业务设计规避。
  • 状态机设计:合理设计状态图,避免过于复杂。可使用子状态机拆分复杂流程,提高可维护性。
  • 监控与运维:记录完整的事务日志,便于问题排查。Seata提供了事务监控界面,可实时查看事务执行状态。

Saga模式特别适用于业务流程长、服务调用多、对最终一致性容忍度较高的场景,如金融渠道层、电商订单链、物流跟踪等系统。

第五章:Seata XA模式:强一致性的分布式事务基石

Seata XA模式是Seata分布式事务解决方案中唯一基于原生XA协议实现强一致性的模式。它通过数据库本身对XA协议的支持,在微服务架构中实现了真正的ACID事务特性,特别适合对数据一致性要求极高的金融、支付等场景。

一、XA模式的核心原理

XA模式基于X/Open组织定义的分布式事务处理(DTP)标准,利用数据库对XA协议的原生支持来管理分支事务。与AT、TCC等补偿型模式不同,XA模式要求事务资源本身参与分布式事务处理,从而保证了真正的全局数据一致性。

📊 XA模式两阶段执行流程

第一阶段:执行阶段

1. XA start:开启XA分支事务
2. 执行SQL:在XA分支中执行业务SQL操作
3. 注册分支:向TC注册分支事务
4. XA end:结束XA分支
5. XA prepare:准备提交,保证持久化

第二阶段:完成阶段

• 分支提交:收到TC的commit命令后执行XA commit
• 分支回滚:收到TC的rollback命令后执行XA rollback

二、Spring Boot集成XA模式实战

XA模式在Spring Boot中的集成与AT模式基本一致,主要区别在于数据源代理的配置。下面通过完整的代码示例展示如何集成Seata XA模式。

1. 添加Maven依赖

<!– Seata Spring Boot Starter –>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.0</version>
</dependency>

<!– MySQL驱动 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>

<!– Druid连接池 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>

2. 配置XA数据源代理

这是XA模式与AT模式的主要区别所在,需要使用DataSourceProxyXA替代DataSourceProxy。

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.xa.DataSourceProxyXA;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfiguration {

/**
* 创建Druid数据源
* 从application.yml读取spring.datasource配置
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}

/**
* 创建XA模式的数据源代理
* 关键点:使用DataSourceProxyXA而不是DataSourceProxy
* 作用:将普通JDBC连接包装为XA连接,透明化XA编程模型
*/
@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
// AT模式使用:return new DataSourceProxy(druidDataSource);
// XA模式使用:DataSourceProxyXA
return new DataSourceProxyXA(druidDataSource);
}
}

3. 应用配置文件

spring:
application:
name: order-service-xa
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000

# Seata配置
seata:
enabled: true
application-id: ${spring.application.name}
# 关键配置:指定使用XA模式
data-source-proxy-mode: XA
tx-service-group: my_test_tx_group
service:
vgroup-mapping:
my_test_tx_group: default
grouplist:
default: 127.0.0.1:8091
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP

4. 业务服务实现

业务层代码与AT模式完全一致,体现了XA模式的无侵入特性。

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

@Autowired
private OrderMapper orderMapper;

@Autowired
private AccountService accountService;

@Autowired
private StorageService storageService;

/**
* 创建订单 – 分布式事务入口
* @GlobalTransactional 注解开启全局事务
* rollbackFor = Exception.class 指定所有异常都回滚
* timeoutMills = 30000 事务超时时间30秒
*/
@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class, timeoutMills = 30000)
@Transactional(rollbackFor = Exception.class)
public void createOrder(OrderDTO orderDTO) {
// 1. 扣减库存(调用库存服务)
storageService.decrease(orderDTO.getProductId(), orderDTO.getCount());

// 2. 扣减余额(调用账户服务)
accountService.decrease(orderDTO.getUserId(), orderDTO.getMoney());

// 3. 创建订单(本地事务)
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setProductId(orderDTO.getProductId());
order.setCount(orderDTO.getCount());
order.setMoney(orderDTO.getMoney());
order.setStatus(0); // 0-创建中
orderMapper.insert(order);

// 4. 更新订单状态
order.setStatus(1); // 1-已创建
orderMapper.updateById(order);

// 如果以上任何一步失败,全局事务会回滚所有操作
}
}

5. 库存服务Feign客户端

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
* 库存服务Feign客户端
* name: 服务注册名称,对应storage-service
* fallback: 熔断降级处理类
*/
@FeignClient(name = "storage-service", fallback = StorageServiceFallback.class)
public interface StorageServiceFeign {

/**
* 扣减库存
* @param productId 商品ID
* @param count 扣减数量
* @return 操作结果
*/
@PostMapping("/storage/decrease")
Result decrease(@RequestParam("productId") Long productId,
@RequestParam("count") Integer count);
}

/**
* 库存服务熔断降级处理
*/
@Component
public class StorageServiceFallback implements StorageServiceFeign {

@Override
public Result decrease(Long productId, Integer count) {
// 记录日志,发送告警
log.error("库存服务调用失败,productId: {}, count: {}", productId, count);
// 抛出异常触发全局事务回滚
throw new RuntimeException("库存服务调用失败,触发分布式事务回滚");
}
}

三、XA模式的适用场景与注意事项

XA模式虽然提供了强一致性保证,但也存在一些限制和需要注意的问题。

📋 XA模式最佳实践指南

✅ 适用场景

  • 金融支付系统:要求强一致性的资金交易
  • 传统XA应用迁移:已有基于XA的应用平滑迁移到Seata
  • 数据库原生支持:MySQL、Oracle、PostgreSQL等主流数据库
  • 短事务场景:事务执行时间可控的业务流程

⚠️ 注意事项

  • 资源锁定时间长:XA prepare后资源被锁定,直到收到commit/rollback
  • 性能影响:相比AT模式,XA模式性能较差
  • 连接保持:需要确保数据库连接在事务期间不中断
  • 超时设置:合理设置事务超时时间,避免长时间锁定

🔧 优化建议

  • 使用连接池管理数据库连接
  • 监控事务执行时间,设置合理超时
  • 对于读多写少场景,考虑AT模式
  • 定期清理长时间挂起的事务

四、部署与监控配置

seata-server配置 (registry.conf)

registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = ""
password = ""
}
}

config {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = ""
password = ""
dataId = "seataServer.properties"
}
}

# 事务日志存储模式(支持file、db、redis)
store {
mode = "db"
db {
datasource = "druid"
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "root"
password = "123456"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}

# 事务超时配置(单位:毫秒)
server {
maxCommitRetryTimeout = -1
maxRollbackRetryTimeout = -1
rollbackRetryTimeoutUnlockEnable = false
}

五、核心价值与演进规划

XA模式的加入补齐了Seata在全局一致性场景下的缺口,形成了AT、TCC、Saga、XA四大事务模式的完整版图。Seata团队为XA模式制定了详细的演进规划,从原型机制到云原生能力的全面打造。

  • 第1步(已完成):1.2.0版本实现XA模式原型机制,确保只增加不修改
  • 第2步:与AT模式必要的融合与重构,优化架构设计
  • 第3步:完善异常处理机制,进行生产环境打磨
  • 第4步:性能优化,提升事务处理效率
  • 第5步:结合Transaction Mesh设计,打造云原生能力

在实际项目中,选择XA模式需要权衡一致性与性能的关系。对于金融、支付等对数据强一致性要求极高的场景,XA模式提供了可靠的解决方案;而对于大多数互联网应用,AT模式在保证最终一致性的同时提供了更好的性能表现。

赞(0)
未经允许不得转载:171主机测评 » 深入剖析 Seata:分布式事务的终极解决方案
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址