在分布式系统中,跨服务操作(如 “下单减库存” 涉及订单服务和库存服务)容易出现 “数据不一致” 问题 —— 订单创建成功但库存未扣减,或库存扣减后订单创建失败。分布式事务技术通过协调多个服务的操作,确保 “要么全成功,要么全回滚”,是保障数据一致性的核心手段。
分布式事务的核心挑战
分布式事务的核心是解决 “跨服务操作的原子性”,面临的挑战:
- 网络不可靠:服务间通信可能超时、丢包
- 服务故障:某服务执行中宕机,无法感知其他服务状态
- 性能损耗:事务协调会增加系统开销,影响吞吐量
主流分布式事务方案
1. 2PC(两阶段提交):强一致性但性能差
2PC 是传统分布式事务方案,分为 “准备阶段” 和 “提交阶段”,由事务协调者(Coordinator)协调所有参与者(Participant)。
流程:
-
准备阶段:协调者向所有参与者发送 “准备提交” 请求,参与者执行操作但不提交,返回 “就绪” 或 “失败”
-
提交阶段:若所有参与者都就绪,协调者发送 “提交” 请求;任一参与者失败,则发送 “回滚” 请求
代码示例 :
// 协调者逻辑
public boolean createOrderAndDeductStock(OrderDTO order) {
// 1. 初始化事务协调者
TwoPhaseCommitCoordinator coordinator = new TwoPhaseCommitCoordinator();
// 2. 注册参与者(订单服务、库存服务)
OrderParticipant orderParticipant = new OrderParticipant(order);
StockParticipant stockParticipant = new StockParticipant(order.getProductId(), order.getNum());
coordinator.registerParticipants(orderParticipant, stockParticipant);
// 3. 执行两阶段提交
return coordinator.commit();
}
// 参与者接口
public interface Participant {
boolean prepare(); // 准备阶段:执行操作,记录日志
boolean commit(); // 提交阶段:确认操作
boolean rollback(); // 回滚阶段:撤销操作
}
优缺点:
- 优点:强一致性,适合对一致性要求极高的场景(如金融交易)
- 缺点:性能差(阻塞等待)、协调者单点风险、长时间锁资源
2. TCC(Try-Confirm-Cancel):高性能的补偿型方案
TCC 将分布式事务拆分为三个阶段,由业务代码实现具体的确认和取消逻辑,更灵活且性能更高。
流程:
-
Try 阶段:资源检查并预留(如检查库存是否充足,冻结部分库存)
-
Confirm 阶段:确认执行业务(如扣减冻结的库存,创建订单)
-
Cancel 阶段:取消操作(如解冻冻结的库存,删除订单草稿)
代码示例(订单 + 库存 TCC) :
// 订单服务TCC接口
public interface OrderTCCService {
// Try:创建订单草稿
@Transactional
String tryCreateOrder(OrderDTO order);
// Confirm:确认创建订单(正式生效)
@Transactional
void confirmCreateOrder(String txId);
// Cancel:取消订单(删除草稿)
@Transactional
void cancelCreateOrder(String txId);
}
// 库存服务TCC接口
public interface StockTCCService {
// Try:检查并冻结库存
@Transactional
String tryDeductStock(Long productId, int num);
// Confirm:扣减冻结的库存
@Transactional
void confirmDeductStock(String txId);
// Cancel:解冻库存
@Transactional
void cancelDeductStock(String txId);
}
// 业务协调逻辑
@Service
public class OrderTransactionService {
@Autowired
private OrderTCCService orderTCC;
@Autowired
private StockTCCService stockTCC;
@Autowired
private TransactionCoordinator coordinator; // 事务协调器(记录各阶段状态)
public boolean createOrder(OrderDTO order) {
String txId = UUID.randomUUID().toString();
try {
// 1. Try阶段:调用所有服务的try方法
String orderTryResult = orderTCC.tryCreateOrder(order);
String stockTryResult = stockTCC.tryDeductStock(order.getProductId(), order.getNum());
// 2. 若所有Try成功,进入Confirm阶段
if ("SUCCESS".equals(orderTryResult) && "SUCCESS".equals(stockTryResult)) {
coordinator.markAsConfirming(txId);
orderTCC.confirmCreateOrder(txId);
stockTCC.confirmDeductStock(txId);
coordinator.markAsCompleted(txId);
return true;
} else {
// 3. 任一Try失败,进入Cancel阶段
throw new RuntimeException("Try阶段失败,触发回滚");
}
} catch (Exception e) {
coordinator.markAsCanceling(txId);
orderTCC.cancelCreateOrder(txId);
stockTCC.cancelDeductStock(txId);
coordinator.markAsCancelled(txId);
return false;
}
}
}
优缺点:
- 优点:性能高(无锁阻塞)、灵活性强(业务自定义补偿逻辑)
- 缺点:侵入性强(需改造业务代码)、补偿逻辑复杂(如 Confirm/Cancel 的幂等性)
3. 最终一致性方案:适合高并发场景
对于一致性要求不高的场景(如订单创建后发送通知),可采用 “本地消息表 + 消息队列” 实现最终一致性:
流程:
-
本地事务:执行主操作(如创建订单),并将 “发送通知” 的消息写入本地消息表
-
消息投递:定时任务扫描本地消息表,将未发送的消息投递到消息队列
-
消息消费:通知服务消费消息,执行发送操作;失败则重试,直到成功
优势:实现简单,性能高;劣势:存在短暂的数据不一致(如订单已创建但通知未发送)
方案选择策略
| 一致性要求 | 性能要求 | 推荐方案 | 典型场景 |
|---|---|---|---|
| 强一致性 | 低 | 2PC | 金融转账、支付结算 |
| 高一致性 | 中 | TCC | 订单创建 + 库存扣减、秒杀 |
| 最终一致性 | 高 | 本地消息表 + MQ | 通知发送、日志同步 |
分布式事务没有 “银弹”,选择方案时需在 “一致性”“性能”“复杂度” 之间权衡。对于大多数业务场景,“最终一致性 + 补偿机制” 是更务实的选择 —— 既能保证数据最终正确,又能兼顾系统的吞吐量和可维护性。