TCC 与 Saga 分布式事务:最终一致性实战指南
🔄 TCC 与 Saga 分布式事务:最终一致性实战指南
文章目录
- 🔄 TCC 与 Saga 分布式事务:最终一致性实战指南
- 🌐 一、分布式事务的最终一致性需求
- ❓ 为什么需要最终一致性?
- 📊 业务场景分析
- ⚡ 二、TCC(Try-Confirm-Cancel)模式
- 🔄 模型原理详解
- 💻 TCC Java 实现示例
- 🏪 TCC 实战案例:电商下单
- 🔄 三、Saga 模式
- 📋 模型原理详解
- 💻 Saga Java 实现示例
- 🏪 Saga 实战案例:旅行预订
- ⚖️ 四、TCC 与 Saga 对比分析
- 📊 技术特性对比
- 🎯 使用场景指南
- 🔧 架构选型决策树
- 🚀 五、总结与实践建议
- 💡 核心要点总结
- 🛠️ 生产环境最佳实践
🌐 一、分布式事务的最终一致性需求
❓ 为什么需要最终一致性?
在微服务架构下,传统的 ACID 事务难以满足跨服务的数据一致性需求。强一致性方案(如 2PC)存在性能瓶颈和可用性问题。
最终一致性的核心价值:
- 高可用性:允许短暂的数据不一致,保证系统可用
- 高性能:异步处理,避免同步阻塞
- 可扩展性:支持大规模分布式系统
📊 业务场景分析
电商下单流程的分布式事务挑战:
传统方案的局限性:
// 强一致性事务在分布式环境下的问题
@Service
public class OrderService {@Transactional // 本地事务,无法跨服务public void createOrder(OrderRequest request) {// 1. 创建订单(本地数据库)orderRepository.save(order);// 2. 调用库存服务(跨服务,不在事务内)inventoryService.deductStock(request.getProductId(), request.getQuantity());// 3. 如果库存服务失败,订单无法回滚!}
}
⚡ 二、TCC(Try-Confirm-Cancel)模式
🔄 模型原理详解
TCC 三阶段核心思想:
- Try:资源预留阶段,锁定必要资源
- Confirm:确认执行,真正执行业务操作
- Cancel:取消操作,释放预留资源
TCC 事务流程图:
💻 TCC Java 实现示例
TCC 事务协调器:
@Component
public class TccTransactionCoordinator {private final Map<String, TccTransaction> transactions = new ConcurrentHashMap<>();/*** 开始TCC事务*/public String beginTransaction() {String transactionId = UUID.randomUUID().toString();transactions.put(transactionId, new TccTransaction(transactionId));return transactionId;}/*** 注册TCC参与者*/public void registerParticipant(String transactionId, TccParticipant participant) {TccTransaction transaction = transactions.get(transactionId);if (transaction != null) {transaction.addParticipant(participant);}}/*** 提交TCC事务*/public boolean commit(String transactionId) {TccTransaction transaction = transactions.get(transactionId);if (transaction == null) {return false;}// Try阶段:资源预留List<Boolean> tryResults = new ArrayList<>();for (TccParticipant participant : transaction.getParticipants()) {try {boolean success = participant.try();tryResults.add(success);} catch (Exception e) {tryResults.add(false);}}// 检查Try结果boolean allTrySuccess = tryResults.stream().allMatch(Boolean::booleanValue);if (allTrySuccess) {// Confirm阶段:确认执行for (TccParticipant participant : transaction.getParticipants()) {participant.confirm();}transactions.remove(transactionId);return true;} else {// Cancel阶段:取消操作for (TccParticipant participant : transaction.getParticipants()) {participant.cancel();}transactions.remove(transactionId);return false;}}
}
库存服务 TCC 实现:
@Service
public class InventoryTccParticipant implements TccParticipant {private final InventoryService inventoryService;private final InventoryLockRepository lockRepository;@Overridepublic boolean try() {// Try阶段:预扣库存(资源预留)String lockId = UUID.randomUUID().toString();boolean locked = inventoryService.lockInventory(getProductId(), getQuantity(), lockId);if (locked) {// 记录锁定信息,用于Confirm/Cancel阶段lockRepository.save(new InventoryLock(lockId, getProductId(), getQuantity()));return true;}return false;}@Overridepublic void confirm() {// Confirm阶段:实际扣减库存InventoryLock lock = lockRepository.findByLockId(getLockId());inventoryService.confirmDeduct(lock.getProductId(), lock.getQuantity());lockRepository.delete(lock); // 清理锁定记录}@Overridepublic void cancel() {// Cancel阶段:释放锁定库存InventoryLock lock = lockRepository.findByLockId(getLockId());inventoryService.cancelDeduct(lock.getProductId(), lock.getQuantity());lockRepository.delete(lock);}
}
订单服务 TCC 集成:
@Service
public class OrderServiceWithTcc {private final TccTransactionCoordinator tccCoordinator;public boolean createOrder(OrderRequest request) {String transactionId = tccCoordinator.beginTransaction();try {// 注册TCC参与者tccCoordinator.registerParticipant(transactionId, new OrderTccParticipant(request));tccCoordinator.registerParticipant(transactionId,new InventoryTccParticipant(request.getProductId(), request.getQuantity()));tccCoordinator.registerParticipant(transactionId,new PointsTccParticipant(request.getUserId(), request.getAmount()));// 执行TCC事务return tccCoordinator.commit(transactionId);} catch (Exception e) {tccCoordinator.rollback(transactionId);throw new BusinessException("创建订单失败", e);}}
}
🏪 TCC 实战案例:电商下单
业务时序图:
🔄 三、Saga 模式
📋 模型原理详解
Saga 模式核心思想:
- 正向操作:按顺序执行一系列业务操作
- 补偿操作:为每个正向操作定义对应的回滚操作
- 最终一致性:通过补偿机制保证数据最终一致
Saga 执行模式:
💻 Saga Java 实现示例
Saga 协调器:
@Component
public class SagaCoordinator {private final Map<String, SagaInstance> sagas = new ConcurrentHashMap<>();/*** 执行Saga事务*/public SagaResult executeSaga(String sagaId, List<SagaStep> steps) {SagaInstance saga = new SagaInstance(sagaId, steps);sagas.put(sagaId, saga);try {for (int i = 0; i < steps.size(); i++) {SagaStep step = steps.get(i);// 执行正向操作boolean success = step.execute();if (!success) {// 执行失败,开始补偿return compensate(saga, i);}// 记录执行进度saga.setCurrentStep(i);}return SagaResult.success();} catch (Exception e) {return compensate(saga, saga.getCurrentStep());}}/*** 补偿操作*/private SagaResult compensate(SagaInstance saga, int failedStepIndex) {for (int i = failedStepIndex; i >= 0; i--) {SagaStep step = saga.getSteps().get(i);try {step.compensate();} catch (Exception e) {// 记录补偿失败,需要人工干预log.error("Saga补偿失败: sagaId={}, stepIndex={}", saga.getId(), i, e);}}return SagaResult.failed();}
}
Saga 步骤定义:
public class OrderSagaStep implements SagaStep {private final OrderService orderService;private final OrderRequest request;@Overridepublic boolean execute() {// 正向操作:创建订单return orderService.createOrder(request);}@Overridepublic void compensate() {// 补偿操作:取消订单orderService.cancelOrder(request.getOrderId());}
}public class InventorySagaStep implements SagaStep {private final InventoryService inventoryService;private final String productId;private final int quantity;@Overridepublic boolean execute() {// 正向操作:扣减库存return inventoryService.deductStock(productId, quantity);}@Overridepublic void compensate() {// 补偿操作:恢复库存inventoryService.restoreStock(productId, quantity);}
}
基于消息的 Saga 实现:
@Component
public class MessageBasedSaga {private final SagaStateRepository stateRepository;/*** 开始Saga流程*/public void startOrderSaga(OrderRequest request) {String sagaId = UUID.randomUUID().toString();SagaState initialState = new SagaState(sagaId, "ORDER_CREATED");stateRepository.save(initialState);// 发送创建订单消息kafkaTemplate.send("order-create-topic", new OrderCreateEvent(sagaId, request));}/*** 处理订单创建成功事件*/@KafkaListener(topics = "order-create-success")public void handleOrderCreated(OrderCreatedEvent event) {SagaState state = stateRepository.findBySagaId(event.getSagaId());state.setStatus("INVENTORY_DEDUCTING");stateRepository.save(state);// 发送扣减库存消息kafkaTemplate.send("inventory-deduct-topic",new InventoryDeductEvent(event.getSagaId(), event.getProductId(), event.getQuantity()));}/*** 处理库存扣减失败事件*/@KafkaListener(topics = "inventory-deduct-failed")public void handleInventoryDeductFailed(InventoryDeductFailedEvent event) {SagaState state = stateRepository.findBySagaId(event.getSagaId());// 开始补偿流程kafkaTemplate.send("order-cancel-topic",new OrderCancelEvent(event.getSagaId(), event.getOrderId()));}
}
🏪 Saga 实战案例:旅行预订
旅行预订 Saga 流程:
⚖️ 四、TCC 与 Saga 对比分析
📊 技术特性对比
方案对比矩阵:
特性维度 | TCC 模式 | Saga 模式 | 优劣分析 |
---|---|---|---|
一致性强度 | 强最终一致性 | 弱最终一致性 | ✅ TCC 通过资源预留机制,一致性更强 |
性能影响 | 较高(三阶段操作) | 较低(直接执行) | ✅ Saga 性能更好,延迟更低 |
实现复杂度 | 高(需预留/确认/回滚) | 中(需定义补偿逻辑) | ✅ Saga 实现更简单,开发成本低 |
业务侵入性 | 高(需业务改造) | 中(需定义补偿) | ✅ Saga 对现有系统侵入性更低 |
适用场景 | 金融系统、交易核心、库存锁定 | 电商订单、跨服务流程编排 | ✅ 按业务选择,TCC注重一致性,Saga注重灵活性 |
🎯 使用场景指南
TCC 适用场景:
- 资金交易:支付、转账等金融业务
- 库存管理:需要严格防止超卖的场景
- 票务系统:座位、票务等资源竞争场景
Saga 适用场景:
- 业务流程:订单处理、工作流审批
- 数据同步:跨系统数据同步
- 异步任务:长时间运行的业务过程
🔧 架构选型决策树
🚀 五、总结与实践建议
💡 核心要点总结
TCC 模式关键洞察:
- 资源预留机制:通过 Try 阶段避免资源冲突
- 事务原子性:Confirm/Cancel 保证操作最终一致
- 业务改造成本:需要业务层支持三阶段操作
Saga 模式核心价值:
- 流程化处理:适合长事务业务流程
- 补偿机制:通过回滚操作保证一致性
- 松耦合设计:基于消息的异步实现
🛠️ 生产环境最佳实践
TCC 实践建议:
# application.yml TCC配置
tcc:transaction:timeout: 300000 # 事务超时时间5分钟max-retries: 3 # 最大重试次数retry-interval: 5000 # 重试间隔5秒
Saga 实践建议:
@Component
public class SagaBestPractices {/*** Saga 幂等性处理*/@KafkaListener(topics = "order-create-topic")public void handleOrderCreate(OrderCreateEvent event) {// 检查是否已处理(幂等性)if (eventRepository.existsByEventId(event.getEventId())) {return;}// 处理业务orderService.createOrder(event.getOrderRequest());// 记录已处理事件eventRepository.save(new ProcessedEvent(event.getEventId()));}/*** 补偿操作容错*/public void compensateWithRetry(SagaStep step, int maxRetries) {for (int i = 0; i < maxRetries; i++) {try {step.compensate();return;} catch (Exception e) {if (i == maxRetries - 1) {// 最后一次重试失败,告警人工干预alertService.sendAlert("Saga补偿失败需要人工干预");}}}}
}
在选择分布式事务方案时,需要综合考虑业务需求、团队技术能力和系统约束。建议从简单场景开始,逐步演进到复杂方案。