那是一个月黑风高的晚上,我正准备下班,突然运营同事急冲冲跑过来:"用户下单付款成功了,但是库存没扣减,订单状态还是待支付!"我的内心瞬间凉了半截,这不是典型的分布式事务问题吗?
说起分布式事务,相信很多人都被它折磨过。微服务架构下,一个业务操作往往涉及多个服务,如何保证数据一致性成了让人头疼的问题。今天就聊聊Seata框架中的两种主流解决方案:TCC模式和Saga模式。
TCC(Try-Confirm-Cancel)模式就像开手动挡的车,操作复杂但控制精准。它要求每个参与方都实现三个接口:
Try阶段:预留资源,比如冻结库存而不是直接扣减 Confirm阶段:真正执行业务,确认扣减库存 Cancel阶段:释放资源,解冻库存
@TccAction
public class InventoryTccAction {
@TwoPhaseBusinessAction(name = "freezeInventory")
public boolean freezeInventory(@BusinessActionContextParameter("productId") Long productId,
@BusinessActionContextParameter("quantity") Integer quantity) {
// Try:冻结库存,不是真正扣减
return inventoryService.freezeStock(productId, quantity);
}
public boolean confirm(BusinessActionContext context) {
// Confirm:确认扣减库存
Long productId = (Long) context.getActionContext("productId");
Integer quantity = (Integer) context.getActionContext("quantity");
return inventoryService.confirmDeduct(productId, quantity);
}
public boolean cancel(BusinessActionContext context) {
// Cancel:解冻库存
Long productId = (Long) context.getActionContext("productId");
Integer quantity = (Integer) context.getActionContext("quantity");
return inventoryService.unfreezeStock(productId, quantity);
}
}
TCC的好处是强一致性,要么全成功要么全回滚,数据不会出现中间状态。但代价也很明显:
我曾经用TCC改造过一个电商订单系统,光是处理各种边界情况就花了两个月,代码复杂度翻了好几倍。
Saga模式就像自动挡汽车,使用简单但控制力稍弱。它把一个分布式事务拆分成多个本地事务,每个本地事务都有对应的补偿事务。
Seata支持两种Saga实现方式:
状态机模式:通过JSON配置定义事务流程 注解模式:直接在方法上加注解
@SagaOrchestration
public class OrderSagaOrchestrator {
@SagaOrchestrationStart
public void processOrder(OrderRequest request) {
// 开始Saga事务
}
@SagaTask(compensationMethod = "cancelPayment")
public PaymentResult makePayment(OrderRequest request) {
return paymentService.pay(request);
}
public void cancelPayment(OrderRequest request) {
// 补偿:退款
paymentService.refund(request.getOrderId());
}
@SagaTask(compensationMethod = "restoreInventory")
public void deductInventory(OrderRequest request) {
inventoryService.deduct(request.getProductId(), request.getQuantity());
}
public void restoreInventory(OrderRequest request) {
// 补偿:恢复库存
inventoryService.restore(request.getProductId(), request.getQuantity());
}
}
Saga的优势很明显:
但也有坑需要注意:会出现中间状态,用户可能看到订单已创建但库存还没扣减的情况。
经过几年的实战经验,我总结了一套选择规则:
选TCC的场景:
选Saga的场景:
还有个实用技巧:混合使用。核心链路用TCC保证强一致性,外围流程用Saga提升性能。比如订单支付用TCC,发送短信通知用Saga。
说个我踩过的坑:早期使用Saga时,没考虑补偿操作的幂等性。结果一个补偿操作被重复执行,用户收到了两次退款。从那以后,我所有的补偿操作都会先检查状态,避免重复执行。
public void cancelPayment(OrderRequest request) {
// 先检查状态,避免重复退款
if (paymentService.isRefunded(request.getOrderId())) {
return;
}
paymentService.refund(request.getOrderId());
}
还有个容易忽视的点:监控和告警。分布式事务涉及多个服务,出问题时排查难度极大。建议为每个事务分配唯一ID,记录详细的执行日志,这样出问题时能快速定位。
分布式事务没有完美方案,只有最适合当前业务的选择。你在项目中用过哪种方案?遇到过什么坑?