1. 分布式事务与Seata简介
在微服务架构中,一个业务操作往往需要跨多个服务完成数据一致性保证,这就是分布式事务要解决的核心问题。Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,它通过AT(自动补偿型)、TCC(Try-Confirm-Cancel)、SAGA和XA四种模式,为微服务架构提供了简单易用的事务能力。
我最初接触Seata是在一个电商项目中,当时遇到订单创建后库存扣减失败导致的数据不一致问题。传统本地事务无法跨越服务边界,而Seata通过全局事务ID(XID)将多个服务的本地事务协调为一个全局事务,这正是我们需要的解决方案。
2. Seata核心组件与工作原理
2.1 三大核心组件解析
Seata的架构包含三个关键角色:
- TC(Transaction Coordinator):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
- TM(Transaction Manager):事务管理器,定义全局事务的范围,负责开启、提交或回滚全局事务
- RM(Resource Manager):资源管理器,管理分支事务处理的资源,负责向TC注册分支事务并报告分支事务状态
2.2 AT模式工作流程
以最常用的AT模式为例,其工作流程可分为四个阶段:
- 全局事务开始:TM向TC申请开启一个全局事务,TC生成XID并返回
- 分支事务注册:RM将本地事务作为分支事务注册到TC
- 全局事务提交:TM向TC发起全局提交,TC异步驱动所有分支事务完成提交
- 全局事务回滚:如果任一分支事务失败,TM向TC发起全局回滚,TC通过undo_log进行补偿
重要提示:AT模式依赖于数据库的本地ACID特性,要求业务数据库必须是支持本地事务的关系型数据库
3. Seata Server部署与配置
3.1 环境准备
在开始配置Demo前,需要准备以下环境:
- JDK 1.8+
- MySQL 5.7+(用于存储Seata Server的事务日志)
- Nacos 1.3.0+(作为配置中心和注册中心)
- 示例使用的Seata版本:1.5.2
3.2 下载与安装
从Seata官网或GitHub Release页面下载对应版本:
wget https://github.com/seata/seata/releases/download/v1.5.2/seata-server-1.5.2.tar.gz tar -zxvf seata-server-1.5.2.tar.gz cd seata3.3 存储模式配置
Seata支持三种存储模式:
- file模式(默认):单机模式,事务日志存储在本地文件
- db模式:高可用模式,事务日志存储在数据库
- redis模式:性能较高,但不保证严格的事务ACID
对于生产环境,推荐使用db模式。修改conf/file.conf:
store { mode = "db" db { datasource = "druid" dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata?useSSL=false" user = "seata" password = "seata" minConn = 5 maxConn = 30 } }3.4 注册中心配置
修改conf/registry.conf使用Nacos作为注册中心:
registry { type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8848" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } }4. 业务系统集成Seata Client
4.1 添加依赖
在Spring Boot项目中添加Seata依赖:
<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.5.2</version> </dependency> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>1.4.2</version> </dependency>4.2 配置参数
在application.yml中添加配置:
seata: application-id: ${spring.application.name} tx-service-group: my_test_tx_group service: vgroup-mapping: my_test_tx_group: default registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 namespace: "" cluster: default config: type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: "" group: SEATA_GROUP4.3 数据源代理配置
Seata需要通过代理数据源来拦截SQL执行,创建代理数据源:
@Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource druidDataSource() { return new DruidDataSource(); } @Primary @Bean("dataSource") public DataSource dataSource(DruidDataSource druidDataSource) { return new DataSourceProxy(druidDataSource); } }5. 完整Demo示例:分布式事务实战
5.1 场景设计
模拟电商系统中的创建订单场景:
- 订单服务:创建订单记录
- 库存服务:扣减商品库存
- 账户服务:扣减用户余额
5.2 全局事务开启
在订单服务的入口方法添加@GlobalTransactional注解:
@Service public class OrderServiceImpl implements OrderService { @GlobalTransactional(name = "create-order", rollbackFor = Exception.class) public Order createOrder(OrderRequest request) { // 1. 创建订单 orderMapper.create(request); // 2. 扣减库存 storageFeignClient.deduct(request.getCommodityCode(), request.getCount()); // 3. 扣减余额 accountFeignClient.debit(request.getUserId(), request.getMoney()); return getOrder(request.getOrderNo()); } }5.3 分支事务实现
库存服务的扣减库存方法:
@Service public class StorageServiceImpl implements StorageService { @Transactional public void deduct(String commodityCode, int count) { Storage storage = storageMapper.findByCommodityCode(commodityCode); if(storage.getCount() < count) { throw new RuntimeException("库存不足"); } storageMapper.deduct(commodityCode, count); } }5.4 异常模拟与回滚测试
为了验证分布式事务是否生效,可以在账户服务中故意抛出异常:
@Service public class AccountServiceImpl implements AccountService { @Transactional public void debit(String userId, BigDecimal money) { // 模拟异常 if(money.compareTo(new BigDecimal("1000")) > 0) { throw new RuntimeException("金额过大"); } accountMapper.debit(userId, money); } }当金额超过1000时,整个分布式事务应该回滚,订单记录会被删除,库存也会恢复。
6. 常见问题排查与优化建议
6.1 XID未传递问题
现象:分支事务没有加入到全局事务中 解决方案:
- 确保服务间调用时XID通过请求头传递
- 检查Feign的拦截器配置:
public class SeataFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String xid = RootContext.getXID(); if (StringUtils.isNotBlank(xid)) { template.header(RootContext.KEY_XID, xid); } } }6.2 连接池配置建议
Seata AT模式会占用数据库连接,建议:
- 适当增大连接池大小
- 设置合理的超时时间
spring: datasource: druid: initial-size: 5 min-idle: 5 max-active: 30 max-wait: 600006.3 性能优化配置
高并发场景下的优化建议:
- 开启Seata的异步化:
seata: client: rm: async-commit-buffer-limit: 10000 tm: commit-retry-count: 5 rollback-retry-count: 5 service: disable-global-transaction: false- 调整undo_log表索引:
ALTER TABLE undo_log ADD INDEX ix_log_status (status);7. 生产环境部署建议
7.1 高可用部署
Seata Server的高可用部署方案:
- 部署多个Seata Server实例
- 使用Nacos等注册中心进行服务发现
- 数据库使用主从架构
- 配置TC集群:
service { vgroupMapping.my_test_tx_group = "default" default.grouplist = "192.168.1.1:8091,192.168.1.2:8091" }7.2 监控与告警
建议集成Prometheus监控:
- 添加依赖:
<dependency> <groupId>io.seata</groupId> <artifactId>seata-metrics-prometheus</artifactId> <version>1.5.2</version> </dependency>- 配置暴露端点:
management: endpoints: web: exposure: include: prometheus metrics: tags: application: ${spring.application.name}7.3 灾备方案
- 定期备份undo_log表
- 配置事务恢复策略:
seata: client: undo: log-save-days: 7 log-delete-period: 86400000