'# Seata服务的搭建、Seata AT模式演示
一、背景与问题
在微服务架构中,分布式事务是系统设计中最复杂的部分之一。传统单体应用中的事务机制无法直接应用于分布式环境,因为业务操作可能涉及多个服务、多个数据库甚至多个网络节点。
Seata(Simple Elastic Transaction Architecture)作为阿里巴巴开源的分布式事务框架,提供了多种解决方案。其中AT(Automatic Transaction)模式是其核心实现之一,通过"一阶段提交+二阶段回滚"的机制,实现了对分布式事务的强一致性保障。
在实际开发中,我们常遇到以下问题:
- 跨服务的库存扣减和订单创建需要原子性
- 分布式系统中出现网络分区导致的数据不一致
- 高并发场景下的事务性能瓶颈
- 异构系统间的数据同步问题
Seata的AT模式通过引入全局事务协调器(TC)、事务管理器(TM)和资源管理器(RM)三者协作,解决了上述问题。
二、基本原理
Seata的AT模式基于两阶段提交协议,其核心机制如下:
一阶段:本地事务准备
- 业务服务在执行业务操作前,会向TC注册事务
- 通过代理数据源,将业务操作记录为"准备提交"状态
- 对数据库进行读写操作,但不提交事务
- 通过行锁机制防止脏读
二阶段:事务提交或回滚
- TC根据全局事务状态决定提交或回滚
- 提交:TC通知所有RM提交事务,释放锁
- 回滚:TC通知所有RM回滚事务,执行补偿操作
- 通过Undo Log实现回滚操作
AT模式的关键在于:
- 通过MySQL的binlog实现数据恢复
- 通过全局事务ID(GTID)进行事务追踪
- 通过行锁机制保障事务隔离性
三、环境准备
3.1 系统要求
- 操作系统:Linux/Windows/MacOS
- Java环境:JDK 1.8+
- 数据库:MySQL 5.6+
- 网络:支持TCP/IP通信
3.2 安装Seata Server
使用Docker快速部署:
docker pull seataio/seata-server:1.6.3
docker run -d --name seata-server \
-p 8091:8091 \
-v /mydata/seata/config:/root/seata/config \
-v /mydata/seata/logs:/root/seata/logs \
seataio/seata-server:1.6.3配置文件file.conf关键配置:
seata:
service:
vgroupMapping:
default: 192.168.1.100:8091
grouplist: 192.168.1.100:8091
config:
name: file
type: file
file:
name: file.conf3.3 数据库准备
创建Seata需要的数据库和表:
CREATE DATABASE seata;
USE seata;
CREATE TABLE `branch_table` (
`branch_id` BIGINT(20) NOT NULL,
`transaction_id` BIGINT(20) NOT NULL,
`resource_group_id` VARCHAR(64) NOT NULL,
`branch_type` VARCHAR(64) NOT NULL,
`branch_status` TINYINT NOT NULL,
`lock_key` VARCHAR(128) NOT NULL,
`branch_retry_count` INT NOT NULL,
`last_update_time` DATETIME NOT NULL,
PRIMARY KEY (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `global_table` (
`xid` VARCHAR(128) NOT NULL,
`transaction_type` VARCHAR(64) NOT NULL,
`transaction_status` TINYINT NOT NULL,
PRIMARY KEY (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;四、核心实现
4.1 事务注解配置
在Spring Boot项目中配置Seata的事务管理:
@Configuration
@EnableTransactionManagement
public class SeataConfig {
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner("order-service", "default");
}
}4.2 事务注解使用
在业务方法上添加@GlobalTransactional注解:
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService;
@GlobalTransactional
public void createOrder(String userId, String productId, int quantity) {
// 1. 创建订单
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
orderRepository.save(order);
// 2. 扣减库存
inventoryService.reduceStock(productId, quantity);
}
}4.3 事务传播机制
在分布式系统中,事务的传播方式至关重要:
@Service
public class InventoryService {
@Autowired
private InventoryRepository inventoryRepository;
@GlobalTransactional
public void reduceStock(String productId, int quantity) {
Inventory inventory = inventoryRepository.findById(productId).get();
inventory.setStock(inventory.getStock() - quantity);
inventoryRepository.save(inventory);
}
}关键代码解释:
@GlobalTransactional注解标记方法为全局事务- Seata会自动创建全局事务ID(xid)
- 通过代理数据源进行数据库操作
- 在事务提交前会注册分支事务
五、完整案例
5.1 项目结构
seata-demo/
├── order-service/
│ ├── src/
│ │ └── main/
│ │ └── java/
│ │ └── com.example
│ │ ├── config/
│ │ │ └── SeataConfig.java
│ │ ├── service/
│ │ │ └── OrderService.java
│ │ └── repository/
│ │ └── OrderRepository.java
│ └── pom.xml
├── inventory-service/
│ ├── src/
│ │ └── main/
│ │ └── java/
│ │ └── com.example
│ │ ├── config/
│ │ │ └── SeataConfig.java
│ │ ├── service/
│ │ │ └── InventoryService.java
│ │ └── repository/
│ │ └── InventoryRepository.java
│ └── pom.xml
└── seata-server/
└── docker-compose.yml5.2 数据库表结构
订单表:
CREATE TABLE `order` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(64) NOT NULL,
`product_id` VARCHAR(64) NOT NULL,
`quantity` INT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;库存表:
CREATE TABLE `inventory` (
`id` VARCHAR(64) NOT NULL,
`product_id` VARCHAR(64) NOT NULL,
`stock` INT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;5.3 事务调用流程
- 客户端发起创建订单请求
OrderService的createOrder方法被标记为全局事务- 创建订单后调用
InventoryService的reduceStock方法 - Seata为整个事务生成全局事务ID(xid)
- 两个服务分别注册分支事务
- 如果所有操作成功,TC发起提交
- 如果任一操作失败,TC发起回滚
六、源码解析
6.1 事务管理器初始化
在Spring Boot中,Seata通过GlobalTransactionScanner进行初始化:
public class GlobalTransactionScanner {
public GlobalTransactionScanner(String transactionName, String defaultTransactionGroup) {
this.transactionName = transactionName;
this.defaultTransactionGroup = defaultTransactionGroup;
this.globalTransactionRepository = new GlobalTransactionRepository();
this.branchTransactionRepository = new BranchTransactionRepository();
this.transactionStatusRepository = new TransactionStatusRepository();
this.transactionLogRepository = new TransactionLogRepository();
}
}关键点:
- 通过配置文件获取事务组信息
- 初始化各个事务相关仓库
- 注册到Spring的Bean工厂
6.2 分支事务注册
在业务方法执行时,Seata会通过TransactionManager进行注册:
public class TransactionManager {
public void registerBranchTransaction(BranchTransaction branchTransaction) {
branchTransactionRepository.save(branchTransaction);
transactionStatusRepository.save(new TransactionStatus(branchTransaction.getXid(), TransactionStatus.STATUS_ACTIVE));
}
}6.3 事务回滚机制
当发生异常时,Seata通过RollbackManager执行回滚:
public class RollbackManager {
public void rollback(String xid) {
List<BranchTransaction> branchTransactions = branchTransactionRepository.findByXid(xid);
for (BranchTransaction branch : branchTransactions) {
branch.getTransactionStatus().setStatus(TransactionStatus.STATUS_ROLLBACK);
branch.getTransactionStatus().setRollbackReason("system error");
branch.getTransactionStatus().setRollbackTime(new Date());
branchTransactionRepository.save(branch);
}
}
}七、进阶使用
7.1 事务超时配置
在复杂业务场景中,需要合理设置事务超时时间:
seata:
tx:
timeout: 30000
rollback-on-timeout: true7.2 多数据源支持
在涉及多个数据库的场景中,需要配置多个数据源:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.inventory")
public DataSource inventoryDataSource() {
return DataSourceBuilder.create().build();
}
}7.3 Spring Cloud整合
在微服务架构中,需要配置事务组和事务协调器:
spring:
cloud:
alibaba:
seata:
enabled: true
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: 192.168.1.100:8091八、性能与工程实践
8.1 性能优化策略
调整事务日志刷盘策略:
seata: log: async: true print: false优化SQL执行计划:
EXPLAIN SELECT * FROM inventory WHERE product_id = 'P001';调整事务隔离级别:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
8.2 安全风险分析
事务泄露风险:
- 原因:未正确关闭事务
- 解决:使用try-with-resources或finally块
SQL注入风险:
- 原因:直接拼接SQL语句
- 解决:使用预编译语句
网络分区风险:
- 原因:TC节点不可达
- 解决:配置多TC节点和重试机制
8.3 方案比较
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| AT模式 | 实现简单,无需改造业务代码 | 性能开销较大 | 需要强一致性 |
| TCC模式 | 适用复杂业务场景 | 需要业务代码改造 | 高并发写操作 |
| Saga模式 | 简单业务场景 | 一致性弱 | 简单业务流程 |
九、常见问题与踩坑
9.1 常见错误
事务未正确传播:
- 原因:未使用
@GlobalTransactional注解 - 解决:确保所有参与事务的方法都标注
- 原因:未使用
资源管理器未注册:
- 原因:未配置数据源代理
- 解决:检查
seata.tx-service-group配置
数据库锁竞争:
- 原因:高并发写操作
- 解决:调整事务隔离级别为READ_COMMITTED
9.2 错误示例
// 错误示例:未使用全局事务注解
public void createOrder() {
orderRepository.save(order);
inventoryService.reduceStock();
}9.3 修复方案
// 正确示例:使用全局事务注解
@GlobalTransactional
public void createOrder() {
orderRepository.save(order);
inventoryService.reduceStock();
}十、最佳实践
10.1 推荐使用场景
- 强一致性要求:如金融交易、库存扣减等场景
- 跨服务调用:多个微服务间的业务操作
- 数据一致性要求:需要保证最终一致性
10.2 不推荐使用场景
- 高并发写操作:可能导致事务锁竞争
- 复杂业务逻辑:需要更细粒度的控制
- 异构系统集成:需要额外适配
10.3 推荐配置
seata:
service:
vgroup-mapping:
default: 192.168.1.100:8091
grouplist: 192.168.1.100:8091
config:
name: file
type: file
file:
name: file.conf
tx:
timeout: 30000
rollback-on-timeout: true十一、总结
Seata的AT模式通过引入全局事务协调器、事务管理器和资源管理器的协作机制,解决了分布式系统中的事务一致性问题。在实际开发中,我们需要根据业务场景选择合适的事务模式,合理配置事务参数,注意事务的传播机制和资源管理。
在使用过程中,需要特别注意事务的性能开销、安全性风险以及可能出现的异常情况。通过合理的事务管理策略,可以有效保障系统的数据一致性,同时避免因事务问题导致的系统故障。
对于需要强一致性的业务场景,Seata的AT模式是一个可靠的选择。但在高并发写操作或复杂业务场景中,需要结合其他模式(如TCC)进行混合使用,以达到最佳的系统性能和一致性保障。