一、为什么分布式事务让人头疼
想象一下这样的场景:你在网上商城下单,系统需要同时完成库存扣减、订单创建和支付三个操作。如果这三个服务分别运行在不同的服务器上,当支付完成后突然库存服务崩溃了,这时候就会出现数据不一致的问题 - 钱扣了但库存没减少。这就是典型的分布式事务问题。
在微服务架构中,每个服务都有自己的数据库,传统的数据库事务(ACID)在这里完全失效。我们需要新的解决方案来保证"要么全部成功,要么全部回滚"的特性。
二、常见的分布式事务解决方案
1. 两阶段提交(2PC)
两阶段提交就像班级选举班长:
- 准备阶段:老师询问每个同学"选小明当班长行不行?"
- 提交阶段:如果所有人都说行,就正式宣布结果;如果有人反对,就取消选举
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模式就像网购的"下单-付款-发货"流程:
- Try:预留资源(冻结库存)
- Confirm:确认操作(实际扣减)
- 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();
}
}
优点:
- 适合长周期业务流程
- 服务间松耦合
缺点:
- 实现复杂度高
- 只能保证最终一致性
四、消息队列的可靠事件模式
这种模式就像寄挂号信:
- 服务A执行本地事务并发送事件到MQ
- 服务B监听MQ并执行自己的事务
- 如果失败,会有重试机制
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;
}
}
优点:
- 高性能
- 服务间完全解耦
缺点:
- 消息可能重复消费
- 只能保证最终一致性
五、如何选择合适的方案
- 强一致性要求高:选择2PC(如金融核心系统)
- 性能要求高:选择TCC或Saga(如电商交易)
- 服务耦合度低:选择可靠事件(如库存与物流系统)
- 长业务流程:选择Saga(如旅行预订系统)
注意事项:
- 分布式事务不可能完美,需要在一致性和性能间权衡
- 越强的一致性通常带来越低的性能
- 一定要有完善的日志和监控,便于问题排查
六、最佳实践建议
- 能避免就避免:通过设计减少分布式事务(如合并服务)
- 最终一致性优先:大多数业务场景其实不需要强一致性
- 做好幂等设计:重试机制必须保证不会重复处理
- 补偿机制要完善:失败后要有明确的回滚路径
- 监控不能少:分布式事务的状态必须可追踪
七、总结
分布式事务是微服务架构下的难点,但没有银弹解决方案。理解每种方案的适用场景和优缺点,根据实际业务需求选择最合适的,才是正确的做法。记住,技术永远是为业务服务的,不要为了技术而技术。
评论