seata实操案例
从Docker启动Seata到Spring Boot集成,再通过一个订单-库存-账户的电商场景,把AT、TCC、SAGA、XA这四种模式的配置和用法都串一遍。
一、环境与版本说明
以下示例基于 Spring Boot 3.2.5 + Seata 1.8.0,这是官方明确支持Spring Boot 3的组合。如果你用的是Spring Boot 2.x,Seata 1.4~1.6也都可以。
组件规划:
- Seata Server(TC):Docker部署,注册到Nacos
- 三个微服务:
order-service(订单)、stock-service(库存)、account-service(账户) - 注册中心:Nacos 2.3.2
- 数据库:MySQL 8.0,每个服务有自己的库
二、Seata Server Docker部署
2.1 拉取并启动(临时获取配置)
docker pull seataio/seata-server:1.8.0
# 临时启动,只是为了拷贝配置文件出来
docker run -d -p 8091:8091 -p 7091:7091 --name seata-temp seataio/seata-server:1.8.0
# 拷贝配置到宿主机
mkdir -p /docker/seata/config
docker cp seata-temp:/seata-server/resources /docker/seata/config
docker rm -f seata-temp
2.2 修改配置 application.yml
关键配置是注册中心和存储模式:
server:
port: 7091
seata:
registry:
type: nacos
nacos:
server-addr: 172.17.0.1:8848 # 容器内访问宿主机Nacos的IP
namespace: public
group: SEATA_GROUP
cluster: default
config:
type: nacos
nacos:
server-addr: 172.17.0.1:8848
namespace: public
group: SEATA_GROUP
data-id: seataServer.properties
store:
mode: db # 生产环境用db,测试用file也行
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.17.0.1:3306/seata_server?useUnicode=true&characterEncoding=utf8
user: root
password: 123456
2.3 正式启动
docker run -d \
--name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-v /docker/seata/config/resources:/seata-server/resources \
seataio/seata-server:1.8.0
⚠️ 注意:如果用
file模式存储,事务日志存本地文件,Server重启后事务状态会丢失。生产环境务必用db模式。
三、Spring Boot 项目集成(通用步骤)
3.1 引入依赖
每个微服务的 pom.xml 都要加:
<!-- Seata Spring Boot Starter -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!-- Spring Cloud Alibaba Seata(如果用了Spring Cloud) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2023.0.1.0</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
这里排除了
spring-cloud-starter-alibaba-seata自带的Seata版本,用我们指定的1.8.0,避免版本冲突。
3.2 配置文件 application.yml
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_tx_group # 事务组名
service:
vgroup-mapping:
my_tx_group: default # 映射到Seata Server的cluster
registry:
type: nacos
nacos:
server-addr: localhost:8848
namespace: public
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: localhost:8848
namespace: public
group: SEATA_GROUP
data-source-proxy-mode: AT # 默认AT,也可改为XA
关键点:
tx-service-group要跟Seata Server端配置的vgroupMapping对应上。
四、数据库准备(AT/XA模式必需)
AT和XA模式依赖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 DEFAULT CHARSET=utf8;
Seata Server自己的库还需要global_table、branch_table、lock_table三张表,从官方GitHub的script/server/db目录获取。
五、AT模式(最常用,无侵入)
AT模式是Seata的默认模式,对业务代码零侵入。
5.1 配置
application.yml中已设置 seata.data-source-proxy-mode: AT(默认就是AT,不配也行)。
5.2 代码示例
订单服务 - 发起方(TM) :
@Service
public class OrderService {
@Autowired
private StockFeignClient stockFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
@Autowired
private OrderMapper orderMapper;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(OrderDTO order) {
// 1. 本地:创建订单
orderMapper.insert(order);
// 2. 远程:扣减库存(调用stock-service)
stockFeignClient.deductStock(order.getProductId(), order.getQuantity());
// 3. 远程:扣减账户余额(调用account-service)
accountFeignClient.decreaseBalance(order.getUserId(), order.getAmount());
}
}
库存服务 - 参与方(RM) :
@Service
public class StockService {
@Autowired
private StockMapper stockMapper;
// 普通本地事务即可,Seata会自动代理DataSource
@Transactional
public void deductStock(Long productId, Integer quantity) {
Stock stock = stockMapper.selectByProductId(productId);
if (stock.getStock() < quantity) {
throw new RuntimeException("库存不足");
}
stock.setStock(stock.getStock() - quantity);
stockMapper.updateById(stock);
}
}
AT模式原理:一阶段执行SQL时自动生成
undo_log(记录修改前后镜像),二阶段如果成功则异步清理undo_log,如果失败则根据undo_log自动回滚。
六、TCC模式(高性能,需编码)
TCC需要手动实现 Try(预留)、Confirm(确认)、Cancel(取消) 三个方法。
6.1 配置
seata:
data-source-proxy-mode: AT # TCC不需要改这个,跟AT共用即可
6.2 定义TCC接口
@LocalTCC
public interface AccountTCCService {
@TwoPhaseBusinessAction(name = "decreaseBalance", commitMethod = "confirm", rollbackMethod = "cancel")
void tryDecreaseBalance(
@BusinessActionContextParameter(paramName = "userId") Long userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount
);
void confirm(BusinessActionContext context);
void cancel(BusinessActionContext context);
}
6.3 实现TCC逻辑
@Service
public class AccountTCCServiceImpl implements AccountTCCService {
@Autowired
private AccountMapper accountMapper;
@Override
@Transactional
public void tryDecreaseBalance(Long userId, BigDecimal amount) {
// Try阶段:检查余额并冻结(预留资源)
Account account = accountMapper.selectByUserId(userId);
if (account.getBalance().compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
// 将可用余额减少,冻结金额增加
account.setFrozen(account.getFrozen().add(amount));
account.setBalance(account.getBalance().subtract(amount));
accountMapper.updateById(account);
}
@Override
@Transactional
public void confirm(BusinessActionContext context) {
// Confirm阶段:真正扣减,释放冻结
Long userId = Long.valueOf(context.getActionContext("userId").toString());
BigDecimal amount = new BigDecimal(context.getActionContext("amount").toString());
Account account = accountMapper.selectByUserId(userId);
account.setFrozen(account.getFrozen().subtract(amount));
accountMapper.updateById(account);
}
@Override
@Transactional
public void cancel(BusinessActionContext context) {
// Cancel阶段:回滚,释放冻结,恢复余额
Long userId = Long.valueOf(context.getActionContext("userId").toString());
BigDecimal amount = new BigDecimal(context.getActionContext("amount").toString());
Account account = accountMapper.selectByUserId(userId);
account.setFrozen(account.getFrozen().subtract(amount));
account.setBalance(account.getBalance().add(amount));
accountMapper.updateById(account);
}
}
6.4 发起方调用
@Service
public class OrderService {
@Autowired
private AccountTCCService accountTCCService;
@GlobalTransactional
public void createOrder(OrderDTO order) {
// ... 订单创建 ...
// 调用TCC服务
accountTCCService.tryDecreaseBalance(order.getUserId(), order.getAmount());
}
}
TCC不依赖数据库事务,性能最好,但开发成本高,适合资金扣减等核心场景。
七、SAGA模式(长事务,流程编排)
SAGA适合流程长、步骤多的场景,通过状态机或注解方式编排正向操作和补偿操作。
7.1 引入依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-saga-starter</artifactId>
<version>1.8.0</version>
</dependency>
7.2 定义状态机(JSON)
resources/statelang/order_saga.json:
{
"name": "orderSaga",
"startState": "CreateOrder",
"states": {
"CreateOrder": {
"type": "ServiceTask",
"serviceName": "orderService",
"serviceMethod": "createOrderLocal",
"compensateMethod": "cancelOrderLocal",
"next": "DeductStock",
"catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
},
"DeductStock": {
"type": "ServiceTask",
"serviceName": "stockService",
"serviceMethod": "deductStockLocal",
"compensateMethod": "restoreStockLocal",
"next": "DecreaseBalance",
"catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
},
"DecreaseBalance": {
"type": "ServiceTask",
"serviceName": "accountService",
"serviceMethod": "decreaseBalanceLocal",
"compensateMethod": "increaseBalanceLocal",
"next": "Success",
"catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
},
"Success": {"type": "Fail", "end": true},
"Failure": {"type": "Fail", "end": true}
}
}
7.3 发起SAGA事务
@Service
public class OrderSagaService {
@Autowired
private StateMachineEngine stateMachineEngine;
@GlobalTransactional
public void createOrder(OrderDTO order) {
Map<String, Object> params = new HashMap<>();
params.put("order", order);
// 启动状态机
stateMachineEngine.start("orderSaga", null, params);
}
}
SAGA模式的官方示例可参考 seata-samples 项目。
八、XA模式(强一致,依赖数据库)
XA模式利用数据库自身的XA协议,强一致性但性能较低。
8.1 配置
seata:
data-source-proxy-mode: XA # 改为XA模式
8.2 代码(跟AT几乎一样)
@Service
public class OrderService {
@GlobalTransactional
public void createOrder(OrderDTO order) {
orderMapper.insert(order);
stockFeignClient.deductStock(order.getProductId(), order.getQuantity());
accountFeignClient.decreaseBalance(order.getUserId(), order.getAmount());
}
}
XA模式代码跟AT一样无侵入,但性能最差(锁资源时间长),适合对一致性要求极高、并发不高的场景。
九、四种模式配置对比
| 模式 | data-source-proxy-mode | 额外依赖 | 额外建表 | 代码侵入 |
|---|---|---|---|---|
| AT | AT(默认) | 无 | undo_log(每个业务库) | 无 |
| TCC | AT 或 TCC | 无 | 无 | 高(需实现3个方法) |
| SAGA | AT | seata-saga-starter | 无 | 中(需定义状态机) |
| XA | XA | 无 | undo_log(每个业务库) | 无 |
十、完整Demo参考
如果想直接看完整可运行的项目,可以参考:
- 官方示例:seata-samples — 包含各模式的Spring Boot示例
- 社区实战:yimo-seata-samples — 同一项目中混合使用AT和TCC模式
- CSDN教程:SpringBoot3.2.5整合Seata1.8.0详细教程 — 完整可运行代码
💡 实操建议
- 先用AT模式跑通:代码改动最小,快速验证链路
- 遇到锁冲突:调大
client.rm.lock.retry.timeout参数 - 跨服务XID传递:用Feign时,
spring-cloud-starter-alibaba-seata会自动传递XID,不用额外处理 - 生产环境:Seata Server至少部署3个节点,存储用DB模式
更多推荐



所有评论(0)