微服务分布式事务解决方案:Saga、TCC、本地消息表深度对比

张开发
2026/4/5 1:36:47 15 分钟阅读

分享文章

微服务分布式事务解决方案:Saga、TCC、本地消息表深度对比
微服务分布式事务解决方案Saga、TCC、本地消息表深度对比别叫我大神叫我 Alex 就好。一、引言大家好我是 Alex。在微服务架构中分布式事务是一个绕不开的话题。传统的 ACID 事务在分布式环境下难以保证我们需要采用新的方案来解决数据一致性问题。今天我想和大家深入对比三种主流的分布式事务解决方案Saga、TCC 和本地消息表。二、分布式事务的挑战1. CAP 定理的限制在分布式系统中我们无法同时满足一致性Consistency所有节点数据一致可用性Availability系统始终可用分区容错性Partition Tolerance网络分区时仍能运行微服务架构通常选择 AP通过最终一致性来保证数据的正确性。2. 传统方案的局限2PC两阶段提交同步阻塞性能差单点故障3PC三阶段提交解决了部分问题但实现复杂三、Saga 模式1. 核心概念Saga 将长事务拆分为多个本地事务每个本地事务有对应的补偿操作T1 → T2 → T3 → ... → Tn如果某个步骤失败执行补偿操作T1 → T2 → T3(失败) → C2 → C12. 实现方式编排式 SagaChoreographyService public class OrderSaga { Autowired private OrderService orderService; Autowired private PaymentService paymentService; Autowired private InventoryService inventoryService; StartSaga SagaEventHandler(associationProperty orderId) public void handle(OrderCreatedEvent event) { SagaLifecycle.associateWith(payment, event.getOrderId()); commandGateway.send(new ProcessPaymentCommand(event.getOrderId(), event.getAmount())); } SagaEventHandler(associationProperty payment) EndSaga public void on(PaymentProcessedEvent event) { SagaLifecycle.associateWith(inventory, event.getOrderId()); commandGateway.send(new ReserveInventoryCommand(event.getOrderId(), event.getItems())); } SagaEventHandler(associationProperty payment) public void on(PaymentFailedEvent event) { // 补偿取消订单 commandGateway.send(new CancelOrderCommand(event.getOrderId())); } }编排式 SagaOrchestrationService public class OrderOrchestrator { public void processOrder(Order order) { try { // 1. 创建订单 orderService.createOrder(order); // 2. 处理支付 paymentService.processPayment(order.getPayment()); // 3. 扣减库存 inventoryService.reserve(order.getItems()); // 4. 发货 shippingService.ship(order); } catch (PaymentException e) { // 补偿取消订单 orderService.cancelOrder(order.getId()); } catch (InventoryException e) { // 补偿退款 取消订单 paymentService.refund(order.getPayment()); orderService.cancelOrder(order.getId()); } } }3. 优缺点分析优点一阶段提交性能好天然适合长事务易于理解和实现缺点隔离性差可能出现脏读补偿逻辑复杂需要业务侵入四、TCC 模式1. 核心概念TCCTry-Confirm-Cancel将每个操作拆分为三个阶段Try预留资源执行业务检查Confirm确认执行业务Cancel取消预留释放资源2. 实现示例public interface PaymentTccAction { TwoPhaseBusinessAction(name paymentAction, commitMethod commit, rollbackMethod rollback) boolean tryPayment(BusinessActionContextParameter(paramName orderId) String orderId, BusinessActionContextParameter(paramName amount) BigDecimal amount); boolean commit(BusinessActionContext context); boolean rollback(BusinessActionContext context); } Service public class PaymentTccActionImpl implements PaymentTccAction { Autowired private PaymentService paymentService; Override public boolean tryPayment(String orderId, BigDecimal amount) { // 冻结账户金额 return paymentService.freezeAmount(orderId, amount); } Override public boolean commit(BusinessActionContext context) { String orderId context.getActionContext(orderId); // 确认支付解冻并扣款 return paymentService.confirmPayment(orderId); } Override public boolean rollback(BusinessActionContext context) { String orderId context.getActionContext(orderId); // 取消支付解冻金额 return paymentService.unfreezeAmount(orderId); } }3. 优缺点分析优点两阶段提交一致性较好资源锁定时间短性能较好缺点业务侵入性强实现复杂Confirm/Cancel 需要幂等五、本地消息表1. 核心概念本地消息表将分布式事务拆分为本地事务和消息发送在业务数据库中创建消息表业务操作和消息写入在同一个本地事务中定时任务扫描消息表发送消息消费方处理消息后更新消息状态2. 实现示例消息表设计CREATE TABLE local_message ( id BIGINT PRIMARY KEY AUTO_INCREMENT, topic VARCHAR(100) NOT NULL, body TEXT NOT NULL, status INT DEFAULT 0, -- 0:待发送, 1:发送中, 2:已发送, 3:消费成功 retry_count INT DEFAULT 0, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_status_create_time (status, create_time) );业务实现Service public class OrderService { Transactional public void createOrder(Order order) { // 1. 保存订单 orderMapper.insert(order); // 2. 写入消息表 LocalMessage message new LocalMessage(); message.setTopic(order_created); message.setBody(JsonUtils.toJson(order)); message.setStatus(0); messageMapper.insert(message); // 同一事务提交 } } Component public class MessageSender { Scheduled(fixedRate 5000) public void sendMessages() { ListLocalMessage messages messageMapper.selectByStatus(0); for (LocalMessage message : messages) { try { // 发送消息 rocketMQTemplate.asyncSend(message.getTopic(), message.getBody(), new SendCallback() { Override public void onSuccess(SendResult sendResult) { messageMapper.updateStatus(message.getId(), 2); } Override public void onException(Throwable e) { messageMapper.incrementRetry(message.getId()); } }); messageMapper.updateStatus(message.getId(), 1); } catch (Exception e) { messageMapper.incrementRetry(message.getId()); } } } }3. 优缺点分析优点实现简单易于理解不依赖外部框架最终一致性保证缺点需要维护消息表实时性较差需要处理幂等六、方案对比与选型特性SagaTCC本地消息表一致性最终一致最终一致最终一致性能高较高中等复杂度中等高低业务侵入中等高低适用场景长事务短事务异步场景七、生产环境建议1. Saga 适用场景业务流程长需要多个服务协作对实时性要求不高可以接受最终一致性2. TCC 适用场景对一致性要求较高资源需要预留业务可以接受复杂度3. 本地消息表适用场景异步处理场景对实时性要求不高希望简单实现八、总结分布式事务没有银弹我们需要根据具体的业务场景选择合适的方案。在实际项目中我通常会组合使用这些方案以达到最佳的效果。这其实可以更优雅一点。希望这篇文章能帮助大家更好地理解和选择分布式事务方案。如果你有任何问题欢迎在评论区留言。关于作者我是 Alex一个在 CSDN 写 Java 架构思考的暖男。喜欢手冲咖啡养了一只叫Java的拉布拉多。如果我的文章对你有帮助欢迎关注我一起探讨 Java 技术的优雅之道。

更多文章