1. 项目背景与核心挑战
在WMS仓库物资管理系统中,我们经常遇到这样的场景:当采购订单入库时,需要同时更新库存记录、生成入库单、扣减供应商应付款。这些操作涉及多个微服务和数据库,如何保证它们要么全部成功,要么全部回滚,就是分布式事务要解决的核心问题。
去年我们系统日均处理10万+库存变动时,就曾因网络抖动导致库存已扣减但入库单未生成,最终花了整整两周人工核对数据。这正是我们引入Seata的契机——它提供了AT和TCC两种成熟的分布式事务模式,经过半年实战检验,系统事务异常率从0.3%降至0.002%。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Seata AT模式原理解析
2.1 AT模式核心机制
AT模式的核心在于"一阶段提交+二阶段补偿"。以采购入库为例:
- 阶段一:各微服务执行本地事务时,Seata会通过代理数据源捕获SQL前后镜像,生成undo_log记录
- 阶段二:事务协调器根据阶段一结果决定全局提交或回滚
关键数据结构示例:
sql复制-- undo_log表结构
CREATE TABLE `undo_log` (
`branch_id` bigint NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL
);
2.2 事务执行流程详解
- 全局事务开始:TM向TC注册全局事务,生成XID(全局唯一事务ID)
- 分支事务注册:RM执行SQL前会向TC注册分支事务
- 本地事务提交:RM提交时,Seata会:
- 前置镜像查询:SELECT * FROM inventory WHERE sku_id='1001' FOR UPDATE
- 执行业务SQL:UPDATE inventory SET qty=qty+100 WHERE sku_id='1001'
- 后置镜像查询:SELECT * FROM inventory WHERE sku_id='1001'
- 全局事务决议:所有分支事务成功后,TC异步删除各节点的undo_log
重要提示:AT模式要求业务表必须有主键,且不支持跨库的UPDATE...LIMIT语句
3. TCC模式在WMS中的实战
3.1 TCC与AT的适用场景对比
| 特性 | AT模式 | TCC模式 |
|---|---|---|
| 侵入性 | 低(无代码侵入) | 高(需实现3个接口) |
| 锁范围 | 全局行锁 | 无锁 |
| 适用场景 | 短事务(<1秒) | 长事务(秒级及以上) |
| 回滚代价 | 较高(依赖undo_log) | 较低(自定义回滚逻辑) |
在WMS中,我们这样划分:
- AT模式:常规库存扣减、单据生成
- TCC模式:月末库存盘点、跨仓调拨等耗时操作
3.2 TCC接口设计实例
以调拨申请为例:
java复制// 调拨服务TCC接口
public interface AllocationService {
@TwoPhaseBusinessAction(name = "prepareAllocate", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(@BusinessActionContextParameter(paramName = "allocationNo") String allocationNo,
@BusinessActionContextParameter(paramName = "fromWarehouse") String fromWh,
@BusinessActionContextParameter(paramName = "toWarehouse") String toWh,
@BusinessActionContextParameter(paramName = "sku") String sku,
@BusinessActionContextParameter(paramName = "qty") int qty);
boolean commit(BusinessActionContext context);
boolean rollback(BusinessActionContext context);
}
实现要点:
- prepare阶段:冻结调出仓库存(状态改为FROZEN)
- commit阶段:实际扣减调出仓库存,增加调入仓库存
- rollback阶段:解除冻结状态
4. WMS中的混合事务方案
4.1 典型业务场景设计
采购入库流程:
- 采购服务(AT):创建采购单
- 库存服务(AT):增加库存
- 财务服务(TCC):冻结应付款
- Try:生成应付冻结记录
- Confirm:转为正式应付
- Cancel:删除冻结记录
4.2 配置关键参数
在seata-server的file.conf中:
properties复制## 事务日志存储模式(生产环境建议用db)
store.mode = db
store.db.datasource = druid
store.db.db-type = mysql
store.db.driver-class-name = com.mysql.jdbc.Driver
store.db.url = jdbc:mysql://127.0.0.1:3306/seata?useSSL=false
store.db.user = seata
store.db.password = seata
## AT模式参数
support.spring.datasource.autoproxy = true
client.rm.report.success.enable = true
5. 踩坑实录与性能优化
5.1 典型问题排查
问题现象:库存扣减时出现"Global lock wait timeout"
- 根因分析:并发事务A和B同时修改同一商品库存
- 事务A先获取全局锁
- 事务B等待600ms(默认超时时间)后失败
- 解决方案:
- 业务层面:引入库存预占机制
- 配置层面:调整client.rm.lock.retryInterval=10ms(默认)和client.rm.lock.retryTimes=30(默认)
5.2 性能优化方案
- undo_log清理:配置定期任务清理7天前的undo_log
sql复制DELETE FROM undo_log WHERE log_created < DATE_SUB(NOW(), INTERVAL 7 DAY); - TC集群部署:采用3节点集群,使用nacos注册中心
- 客户端参数调优:
properties复制# 增加RM处理线程数 client.rm.executor.thread.size=32 # 加大全局事务重试次数 client.tm.commit.retry.count=5
6. 监控与运维实践
6.1 监控指标配置
通过Prometheus监控关键指标:
yaml复制# seata-server监控配置
metrics:
enabled: true
registry-type: compact
exporter-list: prometheus
exporter-prometheus-port: 9898
核心监控项:
- 全局事务成功率(seata.transaction.global.commit.success)
- 平均处理时间(seata.transaction.global.rt)
- 活跃事务数(seata.transaction.active.count)
6.2 日常运维checklist
- 日志分析:重点关注以下异常日志模式:
- "[RM]branch register failed"
- "[TC]global commit retry timeout"
- 定期维护:
- 每月检查undo_log表大小
- 每季度review超时事务日志
- 应急预案:
bash复制# 强制回滚指定XID的事务 curl -X DELETE http://seata-server:8091/tx/transaction/{xid}
在实施过程中我们发现,对于日均百万级事务的WMS系统,将AT模式的全局锁超时时间调整为300ms后,系统吞吐量提升了40%。同时建议对核心商品库存操作采用TCC模式,避免长事务阻塞。
