分布式系統的“后悔藥”:5種補償機制保障你的業務一致性
作者:草捏子
無論是電商訂單的支付失敗,還是跨服務調用的事務中斷,補償機制就像系統的「后悔藥」,幫助我們在故障后止血回滾或最終達成一致。今天,我們深入解析5種核心補償機制。
在分布式系統中,我們常常面臨這樣的靈魂拷問:「如果某個操作中途失敗了,系統如何優雅地回到一致狀態?」
無論是電商訂單的支付失敗,還是跨服務調用的事務中斷,補償機制就像系統的「后悔藥」,幫助我們在故障后止血回滾或最終達成一致。今天,我們深入解析5種核心補償機制。
一、Saga模式:長事務的「分段回滾」
1.1 核心思想
將一個長事務拆解為多個本地事務,每個子事務對應一個補償操作。任何一個步驟失敗,按反向順序觸發補償。
// 訂單服務:創建訂單(正向操作)
public void createOrder(Order order) {
// 持久化訂單到數據庫
}
// 訂單服務:補償操作(取消訂單)
public void cancelOrder(Order order) {
// 標記訂單狀態為已取消
}
// 庫存服務:扣減庫存(正向操作)
public void reduceStock(String productId, int num) {
// 扣減商品庫存
}
// 庫存服務:補償操作(恢復庫存)
public void compensateStock(String productId, int num) {
// 恢復庫存數量
}
二、TCC模式:資源預留的「三階段提交」
2.1 三個階段
圖片
// 積分服務TCC接口
publicinterface PointsService {
@Transactional
boolean tryDeductPoints(Long userId, int points);
@Transactional
boolean confirmDeductPoints(Long userId, int points);
@Transactional
boolean cancelDeductPoints(Long userId, int points);
}
// 實現類示例
@Service
publicclass PointsServiceImpl implements PointsService {
// Try階段:預扣積分
public boolean tryDeductPoints(Long userId, int points) {
// 檢查積分是否充足
// 預扣積分到臨時表
}
// Confirm階段:實際扣減
public boolean confirmDeductPoints(Long userId, int points) {
// 刪除臨時表記錄
// 更新用戶積分
}
// Cancel階段:返還積分
public boolean cancelDeductPoints(Long userId, int points) {
// 從臨時表恢復積分
}
}
三、消息隊列:異步通信的「可靠重試」
3.1 補償流程設計
圖片
// RabbitMQ補償示例
@RabbitListener(queues = "order.queue")
public void handleOrderMessage(OrderMessage message, Channel channel) {
try {
orderService.processOrder(message);
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 記錄重試次數
if (retryCount >= MAX_RETRY) {
channel.basicNack(deliveryTag, false, false);
deadLetterService.sendToDlq(message);
} else {
channel.basicNack(deliveryTag, false, true);
}
}
}
四、狀態機補償:復雜流程的「精準回退」
4.1 狀態流轉設計
圖片
// 訂單狀態機示例
publicclass OrderStateMachine {
private OrderState currentState;
public void transitionTo(OrderState newState) {
// 校驗狀態流轉是否合法
this.currentState = newState;
orderRepository.save(this);
}
public void compensate() {
if (currentState == OrderState.COMMITTED) {
transitionTo(OrderState.COMPENSATING);
// 執行補償邏輯
transitionTo(OrderState.ROLLBACKED);
}
}
}
五、人工補償:最后的「安全網」
當自動化補償失效時,需提供人工操作界面和完整日志追溯:
// 補償任務管理后臺
@RestController
@RequestMapping("/compensation")
publicclass CompensationController {
@GetMapping("/failed-orders")
public List<Order> listFailedOrders() {
return orderRepository.findByStatus(OrderStatus.FAILED);
}
@PostMapping("/manual-compensate")
public void manualCompensate(@RequestParam Long orderId) {
// 人工驗證后觸發補償
orderService.triggerCompensation(orderId);
}
}
如何選擇補償機制?
場景特征 | 推薦機制 |
短事務、強一致性 | 本地事務回滾 |
跨服務長流程 | Saga模式 |
高并發資源操作 | TCC模式 |
異步最終一致性 | 消息隊列+重試 |
復雜狀態流轉 | 狀態機驅動補償 |
最后的小貼士:
- 補償操作必須實現冪等性(多次執行結果一致)
- 記錄完整的操作日志和上下文信息
- 設置補償的超時閾值和重試上限
- 監控系統需要覆蓋補償成功率指標
責任編輯:武曉燕
來源:
草捏子