分布式事务问题的典型场景
微服务拆分后,一个业务操作可能跨多个服务,每个服务有独立的数据库。下单场景涉及订单服务扣减库存、账户服务扣减余额、积分服务增加积分——三个操作必须全部成功或全部回滚。本地事务无法覆盖跨库操作,这就是分布式事务要解决的问题。
Seata的AT模式是对业务代码侵入最小的方案,只需要一个@GlobalTransactional注解。其原理是在SQL执行前后自动生成回滚日志(undo log),事务失败时自动根据undo log反向补偿。
Seata Server部署与Nacos注册中心集成
Seata Server是事务协调器(TC),负责管理全局事务的提交和回滚。生产环境推荐使用Nacos作为注册中心和配置中心。
# docker部署Seata Server
version: '3'
services:
seata-server:
image: seataio/seata-server:2.2.0
container_name: seata-server
ports:
- "8091:8091"
- "7091:7091"
environment:
- SEATA_IP=192.168.1.100
- SEATA_PORT=8091
volumes:
- ./seata-config:/seata-server/resources
restart: always
# seata-config/application.yml 关键配置
seata:
config:
type: nacos
nacos:
server-addr: 192.168.1.10:8848
namespace: seata
group: SEATA_GROUP
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.1.10:8848
namespace: seata
group: SEATA_GROUP
store:
mode: db
db:
datasource: druid
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.10:3306/seata?rewriteBatchedStatements=true
user: seata
password: seata_pwd
min-conn: 5
max-conn: 30
微服务客户端集成:数据源代理与undo log表
每个参与分布式事务的微服务需要配置Seata数据源代理,并在业务库中创建undo_log表。
<!-- pom.xml 依赖 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2023.0.1.0</version>
</dependency>
-- 每个业务库都需要创建undo_log表
CREATE TABLE IF NOT EXISTS `undo_log` (
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal, 1:defensed',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT transaction undo log';
业务代码实现:@GlobalTransactional注解与Feign调用
在业务入口方法上添加@GlobalTransactional注解,Seata通过拦截SQL自动管理事务上下文。
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private AccountClient accountClient;
@Autowired
private InventoryClient inventoryClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public OrderResult createOrder(OrderRequest request) {
// 1. 创建订单(本地事务,Seata自动代理)
Order order = new Order();
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setAmount(request.getAmount());
order.setStatus("CREATED");
orderMapper.insert(order);
// 2. 扣减库存(远程调用,XID自动传播)
inventoryClient.deduct(request.getProductId(), request.getQuantity());
// 3. 扣减账户余额(远程调用)
accountClient.debit(request.getUserId(), request.getAmount());
// 4. 更新订单状态
order.setStatus("PAID");
orderMapper.updateStatus(order);
return OrderResult.success(order.getId());
}
}
// Feign客户端——XID通过请求头自动传播
@FeignClient(name = "account-service", fallback = AccountFallback.class)
public interface AccountClient {
@PostMapping("/account/debit")
Result debit(@RequestParam("userId") Long userId,
@RequestParam("amount") BigDecimal amount);
}
XID(全局事务ID)通过HTTP Header在服务间自动传播,这是Seata Spring Cloud集成的核心机制。Feign拦截器自动将当前XID写入请求头,对端服务自动解析并绑定到当前分支事务。
生产环境调优:超时、重试与全局锁
AT模式的核心风险是全局锁。一阶段本地事务提交后,Seata会对修改的数据加全局锁,直到全局事务提交或回滚才释放。如果全局事务耗时过长,全局锁会阻塞其他事务。
# 超时与重试配置
seata:
client:
rm:
async-commit-buffer-limit: 10000
report-retry-count: 5
lock:
retry-interval: 10 # 全局锁重试间隔(ms)
retry-times: 30 # 全局锁重试次数
tm:
commit-retry-count: 3
rollback-retry-count: 3
default-global-transaction-timeout: 60000 # 全局事务超时60秒
全局事务超时时间需要根据业务场景设置。跨三个服务的订单创建通常5秒内完成,但涉及外部支付网关时可能需要30秒以上。超时后Seata自动回滚,避免全局锁长期占用。
undo log的清理同样重要。已完成的全局事务的undo log默认保留7天,高频业务场景下undo log表可能积累大量数据,影响回滚性能。配置定时清理任务:
@Scheduled(cron = "0 0 3 * * ?")
public void cleanUndoLog() {
int deleted = undoLogMapper.cleanExpiredLogs(
LocalDate.now().minusDays(7)
);
log.info("清理undo_log记录数: {}", deleted);
}
分布式事务没有银弹,Seata AT模式适合对一致性要求高、并发量中等的场景。超高频场景考虑TCC模式或最终一致性方案(如本地消息表+定时补偿),低频但强一致场景用AT模式最省心。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-fen-bu-shi-shi-wu-shi-zhan-seataat-mo/