题目
设计一个跨微服务的分布式事务方案:订单创建与库存扣减
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
分布式事务,最终一致性,Saga模式,消息队列,幂等性设计
快速回答
核心方案要点:
- 采用Saga事务模式替代传统2PC
- 通过消息队列(如Kafka)实现事件驱动架构
- 每个子事务需实现幂等操作和补偿机制
- 使用事务日志表保证本地操作可靠性
- 最终一致性通过重试+人工干预保障
1. 问题场景
在电商系统中,创建订单涉及:
1) 订单服务创建订单记录
2) 库存服务扣减库存
3) 支付服务处理支付
要求保证跨服务数据一致性,同时满足高并发需求。
2. 核心方案:Saga模式实现
执行流程:
// Saga协调器伪代码
public void createOrder(OrderRequest request) {
// 1. 开启Saga事务
String sagaId = generateSagaId();
// 2. 执行订单创建(正向操作)
Order order = orderService.createOrder(request, sagaId);
// 3. 发布库存扣减事件
eventBus.publish(new InventoryEvent(sagaId, order.getItems()));
}
// 库存服务消费者
@EventListener
public void handleInventoryEvent(InventoryEvent event) {
try {
inventoryService.deductStock(event.getItems(), event.getSagaId());
eventBus.publish(new StockDeductedEvent(event.getSagaId()));
} catch (Exception e) {
eventBus.publish(new StockFailedEvent(event.getSagaId()));
}
}3. 关键设计要点
3.1 补偿机制设计
- 订单服务补偿:将订单状态改为"已取消"
- 库存服务补偿:执行库存回滚操作
// 库存补偿示例 @Compensate public void compensateDeduct(String sagaId) { // 根据sagaId查询原始操作日志 DeductLog log = logRepository.findBySagaId(sagaId); // 执行反向操作 inventoryService.addStock(log.getItems()); }
3.2 幂等性保障
- 每个服务维护事务日志表:
CREATE TABLE transaction_log ( saga_id VARCHAR(50) PRIMARY KEY, service_name VARCHAR(20), status ENUM('PROCESSING','SUCCESS','FAILED'), created_at TIMESTAMP ); - 请求前检查saga_id执行状态
- 使用数据库唯一索引防止重复创建
3.3 消息可靠性保障
- 生产者:本地事务+消息表(Transactional Outbox)
- 消费者:
- 至少一次投递(Kafka)
- 消费前校验幂等性
4. 最佳实践
- 超时控制:设置Saga全局超时(e.g. 30分钟)
- 可视化追踪:通过saga_id串联所有服务日志
- 熔断机制:库存服务失败时触发熔断
- 人工干预接口:提供事务状态查询和手动补偿API
5. 常见错误
- ❌ 未实现幂等性导致重复扣减库存
- ❌ 补偿操作未考虑业务约束(如已发货订单不能取消)
- ❌ 未处理消息丢失导致事务悬挂
- ❌ 忽略资源隔离引发雪崩效应
6. 扩展知识
- 对比方案:
- TCC模式:更适合金融场景但实现复杂
- 本地消息表:轻量级但需轮询
- 新兴技术:
- Seata AT模式(自动补偿)
- Event Sourcing+CQRS
- CAP取舍:在P(分区容忍)必选时,AP优于CP