1. 电商交易中的分布式事务挑战
想象一下双十一零点抢购的场景:用户点击下单按钮后,系统需要同时完成库存扣减、生成订单、扣减账户余额三个操作。这三个操作可能分布在不同的微服务中,使用不同的数据库。如果库存扣减成功但账户余额不足,或者订单生成后系统崩溃,就会导致数据不一致——这就是典型的分布式事务问题。
在传统单体架构中,我们用一个数据库事务就能保证ACID特性。但在微服务架构下,Dubbo虽然解决了服务调用的问题,却无法天然保证跨服务的事务一致性。这时候就需要引入分布式事务解决方案,而TCC模式正是为高并发场景量身定制的方案。
我去年参与过一个跨境电商项目,就遇到过这样的问题:促销活动期间,由于海外仓库库存服务响应慢,经常出现订单创建成功但库存未扣减的情况。后来我们引入Seata的TCC模式后,问题才得到彻底解决。
2. TCC模式的核心设计思想
2.1 三阶段事务模型解析
TCC(Try-Confirm-Cancel)模式将事务分为三个阶段:
- Try阶段:预留资源。比如先把库存冻结起来,账户余额先预扣款
- Confirm阶段:确认执行。所有参与者都预留成功后,真正提交变更
- Cancel阶段:取消预留。任一参与者失败时,释放之前预留的资源
这种设计很像我们生活中的酒店预订:先预定房间(Try),到店办理入住(Confirm),或者取消预订(Cancel)。
2.2 与AT模式的本质区别
Seata的AT模式虽然使用简单,但在高并发场景下有两个致命问题:
- 全局锁竞争激烈,容易成为性能瓶颈
- 无法处理非事务型资源(如Redis)
而TCC模式通过业务层面的预留机制,完全避免了数据库行锁竞争。实测在同样的1000TPS压力下,AT模式平均响应时间达到200ms,而TCC模式能保持在50ms以内。
3. Dubbo与Seata的整合实战
3.1 环境准备与配置
首先在pom.xml中添加依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.15</version>
</dependency>
然后在application.yml中配置Seata:
seata:
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
3.2 TCC接口设计规范
TCC模式要求每个事务参与者实现三个方法。以库存服务为例:
public interface StockServiceTcc {
@TwoPhaseBusinessAction(name = "stockTcc", commitMethod = "commit", rollbackMethod = "cancel")
boolean tryDeduct(@BusinessActionContextParameter(paramName = "productId") String productId,
@BusinessActionContextParameter(paramName = "count") int count);
boolean commit(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
关键点说明:
@TwoPhaseBusinessAction注解声明TCC方法@BusinessActionContextParameter保存参数到上下文- commit/cancel方法通过context获取try阶段的参数
4. 电商交易场景完整实现
4.1 库存服务实现
@Service
public class StockServiceTccImpl implements StockServiceTcc {
@Autowired
private StockMapper stockMapper;
@Autowired
private StockLockMapper stockLockMapper;
@Override
public boolean tryDeduct(String productId, int count) {
// 检查可用库存
Stock stock = stockMapper.selectByProductId(productId);
if(stock.getAvailable() < count) {
throw new RuntimeException("库存不足");
}
// 冻结库存
StockLock lock = new StockLock();
lock.setProductId(productId);
lock.setLockCount(count);
lock.setXid(RootContext.getXID());
stockLockMapper.insert(lock);
// 更新可用库存
stockMapper.updateAvailable(productId, -count);
return true;
}
@Override
public boolean commit(BusinessActionContext context) {
String productId = (String)context.getActionContext("productId");
int count = (int)context.getActionContext("count");
// 扣减真实库存
stockMapper.updateStock(productId, -count);
// 清除冻结记录
stockLockMapper.deleteByXid(context.getXid());
return true;
}
@Override
public boolean cancel(BusinessActionContext context) {
String productId = (String)context.getActionContext("productId");
int count = (int)context.getActionContext("count");
// 恢复可用库存
stockMapper.updateAvailable(productId, count);
// 清除冻结记录
stockLockMapper.deleteByXid(context.getXid());
return true;
}
}
4.2 订单服务全局事务控制
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference
private StockServiceTcc stockService;
@DubboReference
private AccountServiceTcc accountService;
@GlobalTransactional(timeoutMills = 300000)
@Override
public Order createOrder(OrderRequest request) {
// Try阶段
stockService.tryDeduct(request.getProductId(), request.getCount());
accountService.tryDebit(request.getUserId(), request.getAmount());
// 创建订单(状态为处理中)
Order order = new Order();
order.setStatus(OrderStatus.PROCESSING);
orderMapper.insert(order);
return order;
}
}
5. 生产环境中的注意事项
5.1 幂等性控制
网络抖动可能导致重复调用,所有TCC方法都要实现幂等。建议在数据库中添加xid作为唯一索引:
ALTER TABLE stock_lock ADD UNIQUE INDEX idx_xid (xid);
5.2 空回滚处理
Try未执行但收到Cancel调用时,需要特殊处理:
@Override
public boolean cancel(BusinessActionContext context) {
// 检查try是否执行过
if(!stockLockMapper.exists(context.getXid())) {
return true; // 空回滚直接返回成功
}
// ...正常回滚逻辑
}
5.3 性能优化建议
- 对热点商品库存使用本地缓存+异步扣减策略
- Seata Server集群部署,使用Redis存储事务日志
- 设置合理的超时时间,避免长时间占用资源
6. 监控与排查技巧
在application.yml中添加监控配置:
management:
endpoints:
web:
exposure:
include: health,info,metrics,seata
通过/metrics端点可以监控:
- seata.transaction.active.count:活跃事务数
- seata.transaction.committed.rate:事务提交率
遇到问题时,可以查询Seata Server控制台的全局事务列表,查看具体失败原因。
334



被折叠的 条评论
为什么被折叠?



