微服务架构下,跨服务的数据一致性是后端开发面临的核心挑战。Seata的AT模式通过两阶段提交和SQL解析实现自动化分布式事务,对业务代码侵入性低。本文记录Spring Boot集成Seata的完整流程。
Seata AT模式工作原理
AT模式分为两个阶段。第一阶段:拦截业务SQL,解析SQL语义找到操作的行记录,在事务提交前保存数据快照(before image)和操作后的数据快照(after image)到undo_log表,并在分支事务表中注册分支事务。第二阶段:如果全局事务决议提交,异步删除undo_log记录;如果决议回滚,根据before image生成反向SQL恢复数据。
核心角色说明:
- TC(Transaction Coordinator):事务协调器,维护全局事务和分支事务状态
- TM(Transaction Manager):事务管理器,定义全局事务范围,负责开启/提交/回滚
- RM(Resource Manager):资源管理器,管理分支事务上的资源,向TC注册分支事务
Seata Server部署配置
使用Docker部署Seata Server,以Nacos作为注册中心和配置中心:
docker run -d --name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-e SEATA_IP=192.168.1.50 \
-e SEATA_PORT=8091 \
-e STORE_MODE=db \
-e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
-v /data/seata/config:/root/seata-config \
apache/seata-server:2.0.0
registry.conf配置文件:
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "192.168.1.40:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "192.168.1.40:8848"
group = "SEATA_GROUP"
namespace = ""
dataId = "seataServer.properties"
}
}
Spring Boot微服务集成Seata客户端
添加Maven依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.3.0</version>
</dependency>
application.yml配置:
seata:
enabled: true
application-id: order-service
tx-service-group: yunthe_tx_group
service:
vgroup-mapping:
yunthe_tx_group: default
grouplist:
default: 192.168.1.50:8091
registry:
type: nacos
nacos:
server-addr: 192.168.1.40:8848
group: SEATA_GROUP
namespace: ""
config:
type: nacos
nacos:
server-addr: 192.168.1.40:8848
group: SEATA_GROUP
namespace: ""
data-source-proxy-mode: AT
业务代码实现:订单创建分布式事务
以电商下单场景为例,涉及订单服务、库存服务和账户服务三个微服务:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFeignClient storageFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
public Order createOrder(OrderDTO dto) {
// 1. 创建订单
Order order = new Order();
order.setUserId(dto.getUserId());
order.setProductId(dto.getProductId());
order.setCount(dto.getCount());
order.setMoney(dto.getMoney());
order.setStatus(0);
orderMapper.insert(order);
// 2. 扣减库存(远程调用库存服务)
Result storageResult = storageFeignClient.decreaseStorage(
dto.getProductId(), dto.getCount()
);
if (!storageResult.isSuccess()) {
throw new RuntimeException("库存扣减失败: " + storageResult.getMessage());
}
// 3. 扣减账户余额(远程调用账户服务)
Result accountResult = accountFeignClient.decreaseAccount(
dto.getUserId(), dto.getMoney()
);
if (!accountResult.isSuccess()) {
throw new RuntimeException("余额扣减失败: " + accountResult.getMessage());
}
// 4. 修改订单状态为已完成
order.setStatus(1);
orderMapper.updateById(order);
return order;
}
}
@GlobalTransactional注解标记全局事务入口。TM向TC发起全局事务开启请求,获取XID。XID通过RPC请求头传递到下游服务,RM自动注册分支事务。任意环节抛出异常,TC协调所有分支事务回滚。
高并发场景下的优化策略
高并发场景下Seata AT模式存在性能瓶颈,主要来自全局锁竞争和undo_log写入开销。优化方案如下:
方案一:热点数据使用Redis预扣减,减少数据库锁持有时间。
@Service
public class StorageService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StorageMapper storageMapper;
@Transactional(rollbackFor = Exception.class)
public void decreaseStorage(Long productId, Integer count) {
String key = "stock:" + productId;
Long remaining = redisTemplate.opsForValue().increment(key, -count.longValue());
if (remaining == null || remaining < 0) {
redisTemplate.opsForValue().increment(key, count.longValue());
throw new RuntimeException("库存不足");
}
// 异步更新数据库
storageMapper.decreaseStorage(productId, count);
}
}
方案二:将同步扣减改为消息中间件异步处理,降低事务持有时间。订单创建成功后发送MQ消息,库存和账户服务消费消息异步扣减。服务治理方面需配合幂等性检查,防止消息重复消费。
undo_log表创建与数据源代理配置
每个参与分布式事务的数据库都需要创建undo_log表:
CREATE TABLE undo_log (
branch_id BIGINT NOT NULL COMMENT '分支事务ID',
xid VARCHAR(128) NOT NULL COMMENT '全局事务ID',
context VARCHAR(128) NOT NULL COMMENT '上下文',
rollback_info LONGBLOB NOT NULL COMMENT '回滚信息',
log_status INT NOT NULL COMMENT '状态',
log_created DATETIME(6) NOT NULL COMMENT '创建时间',
log_modified DATETIME(6) NOT NULL COMMENT '修改时间',
UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE = InnoDB COMMENT = 'AT模式回滚日志表';
Seata自动代理数据源,拦截SQL执行生成undo_log。如果使用多数据源,需要手动配置DataSourceProxy。排查分布式事务问题时,检查TC控制台的事务日志,确认各分支事务状态是否一致。XID日志追踪可以在日志框架中添加MDC过滤器,将XID打印到每条日志中便于关联。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-fen-bu-shi-shi-wu-chu-li-seataat-mo/