一、为什么分布式事务让人头疼

想象一下这样的场景:你在网上商城下单,系统需要同时完成库存扣减、订单创建和支付三个操作。如果这三个服务分别运行在不同的服务器上,当支付完成后突然库存服务崩溃了,这时候就会出现数据不一致的问题 - 钱扣了但库存没减少。这就是典型的分布式事务问题。

在微服务架构中,每个服务都有自己的数据库,传统的数据库事务(ACID)在这里完全失效。我们需要新的解决方案来保证"要么全部成功,要么全部回滚"的特性。

二、常见的分布式事务解决方案

1. 两阶段提交(2PC)

两阶段提交就像班级选举班长:

  1. 准备阶段:老师询问每个同学"选小明当班长行不行?"
  2. 提交阶段:如果所有人都说行,就正式宣布结果;如果有人反对,就取消选举

Java代码示例(使用Atomikos实现):

// 技术栈:Java + Spring Boot + Atomikos
@Configuration
public class TransactionConfig {
    @Bean
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransaction = new UserTransactionImp();
        userTransaction.setTransactionTimeout(300);
        return userTransaction;
    }

    @Bean
    public TransactionManager transactionManager() throws Throwable {
        return new UserTransactionManager();
    }

    @Bean
    public JtaTransactionManager jtaTransactionManager() throws Throwable {
        return new JtaTransactionManager(userTransaction(), transactionManager());
    }
}

@Service
public class OrderService {
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PaymentService paymentService;
    
    @Transactional
    public void createOrder(OrderDTO orderDTO) {
        // 第一阶段:准备
        inventoryService.reduceStock(orderDTO); 
        paymentService.createPayment(orderDTO);
        
        // 第二阶段:提交
        // 如果这里抛出异常,前面两个操作都会回滚
    }
}

优点:

  • 强一致性保证
  • 实现相对简单

缺点:

  • 性能较差(需要等待所有参与者响应)
  • 同步阻塞(参与者故障会导致整个系统挂起)

2. 补偿事务(TCC)

TCC模式就像网购的"下单-付款-发货"流程:

  1. Try:预留资源(冻结库存)
  2. Confirm:确认操作(实际扣减)
  3. Cancel:取消预留(解冻库存)

Java代码示例:

// 技术栈:Java + Spring Boot
@Service
public class OrderTccService {
    @Autowired
    private InventoryTccService inventoryTccService;
    @Autowired
    private PaymentTccService paymentTccService;

    public void createOrder(OrderDTO orderDTO) {
        // 第一阶段:尝试
        inventoryTccService.tryReduceStock(orderDTO);
        paymentTccService.tryCreatePayment(orderDTO);
        
        try {
            // 第二阶段:确认
            inventoryTccService.confirmReduceStock(orderDTO);
            paymentTccService.confirmCreatePayment(orderDTO);
        } catch (Exception e) {
            // 出现异常则取消
            inventoryTccService.cancelReduceStock(orderDTO);
            paymentTccService.cancelCreatePayment(orderDTO);
            throw e;
        }
    }
}

优点:

  • 性能较好
  • 避免了长时间资源锁定

缺点:

  • 实现复杂(需要为每个操作编写三个方法)
  • 业务侵入性强

三、更现代的解决方案:Saga模式

Saga模式就像旅行计划:

  • 按顺序执行各个步骤(订机票→订酒店→租车)
  • 如果某步失败,就逆向执行补偿操作(取消租车→退酒店→退机票)

Java代码示例(使用Axon Framework):

// 技术栈:Java + Spring Boot + Axon Framework
@Saga
public class OrderSaga {
    @Autowired
    private transient CommandGateway commandGateway;
    
    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // 第一步:扣减库存
        commandGateway.send(new ReduceInventoryCommand(
            event.getOrderId(),
            event.getProductId(),
            event.getAmount()
        ));
    }
    
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(InventoryReducedEvent event) {
        // 第二步:创建支付
        commandGateway.send(new CreatePaymentCommand(
            event.getOrderId(),
            event.getAmount()
        ));
    }
    
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(PaymentCreatedEvent event) {
        // 第三步:完成订单
        commandGateway.send(new CompleteOrderCommand(
            event.getOrderId()
        ));
        SagaLifecycle.end();
    }
    
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(InventoryReductionFailedEvent event) {
        // 库存扣减失败,取消订单
        commandGateway.send(new CancelOrderCommand(event.getOrderId()));
        SagaLifecycle.end();
    }
}

优点:

  • 适合长周期业务流程
  • 服务间松耦合

缺点:

  • 实现复杂度高
  • 只能保证最终一致性

四、消息队列的可靠事件模式

这种模式就像寄挂号信:

  1. 服务A执行本地事务并发送事件到MQ
  2. 服务B监听MQ并执行自己的事务
  3. 如果失败,会有重试机制

Java代码示例(使用RocketMQ):

// 技术栈:Java + Spring Boot + RocketMQ
@Service
public class OrderEventService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Transactional
    public void createOrder(OrderDTO orderDTO) {
        // 1. 本地事务
        orderMapper.create(orderDTO);
        
        // 2. 发送事件
        rocketMQTemplate.sendMessageInTransaction(
            "order-topic",
            MessageBuilder.withPayload(
                new OrderCreatedEvent(orderDTO.getId(), orderDTO.getAmount())
            ).build(),
            orderDTO
        );
    }
}

// 事务监听器
@RocketMQTransactionListener
public class OrderTransactionListener implements RocketMQLocalTransactionListener {
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        try {
            // 执行本地事务
            OrderDTO orderDTO = (OrderDTO) arg;
            inventoryMapper.reduce(orderDTO.getProductId(), orderDTO.getAmount());
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }
    
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        // 检查本地事务状态
        OrderCreatedEvent event = (OrderCreatedEvent) msg.getPayload();
        return inventoryMapper.checkReduction(event.getOrderId()) 
            ? RocketMQLocalTransactionState.COMMIT
            : RocketMQLocalTransactionState.ROLLBACK;
    }
}

优点:

  • 高性能
  • 服务间完全解耦

缺点:

  • 消息可能重复消费
  • 只能保证最终一致性

五、如何选择合适的方案

  1. 强一致性要求高:选择2PC(如金融核心系统)
  2. 性能要求高:选择TCC或Saga(如电商交易)
  3. 服务耦合度低:选择可靠事件(如库存与物流系统)
  4. 长业务流程:选择Saga(如旅行预订系统)

注意事项:

  • 分布式事务不可能完美,需要在一致性和性能间权衡
  • 越强的一致性通常带来越低的性能
  • 一定要有完善的日志和监控,便于问题排查

六、最佳实践建议

  1. 能避免就避免:通过设计减少分布式事务(如合并服务)
  2. 最终一致性优先:大多数业务场景其实不需要强一致性
  3. 做好幂等设计:重试机制必须保证不会重复处理
  4. 补偿机制要完善:失败后要有明确的回滚路径
  5. 监控不能少:分布式事务的状态必须可追踪

七、总结

分布式事务是微服务架构下的难点,但没有银弹解决方案。理解每种方案的适用场景和优缺点,根据实际业务需求选择最合适的,才是正确的做法。记住,技术永远是为业务服务的,不要为了技术而技术。