Chuyển đến nội dung chính

第 8 課:Saga 模式與分散式事務

為什麼 ACID 在微服務中不起作用?傳奇模式:編排與編排。補償事務、冪等性和錯誤處理。實際範例:訂單→付款→庫存→運送工作流程。

🏗️ 建築 — 第 8 課 第 8 課:Saga 模式與分佈式 交易

微服務與微前端系統設計-從基礎到生產

第 3 部分:微服務中的資料架構

亞洲開發網

簡介

在 Monolith 中,交易很簡單: BEGIN → INSERT order → UPDATE inventory → COMMIT。在微服務中,每個服務都有自己的DB→無法使用分散式ACID事務(2PC太慢、脆弱)。 傳奇模式是標準解決方案。

Saga 模式-分散式事務的編排與編排


1. 問題:分散式事務

1.1 為什麼不適合2PC(兩階段提交)?

  • 封鎖:所有參與者都被鎖定,直到協調員決定
  • 單點故障:協調器崩潰→死鎖
  • 效能受到影響:延遲顯著增加
  • 無規模:新增參與者=增加複雜度指數

1.2 接受最終一致性

在微服務中,我們接受最終一致性而不是強一致性。數據將最終一致,而不是立即。


2.傳奇模式

Saga = 本地事務的字串,每個事務更新1個服務。如果交易失敗,補償事務將撤銷先前的變更。

2.1 基於編排的傳奇

每個服務發布事件→下一個服務監聽並執行:

Order Service         Payment Service       Inventory Service
     │                      │                      │
     │──OrderCreated───────►│                      │
     │                      │──PaymentProcessed───►│
     │                      │                      │──InventoryReserved──►
     │                      │                      │
     │ (nếu Inventory fail)                        │
     │                      │◄──InventoryFailed────│
     │◄──PaymentRefunded────│                      │
     │──OrderCancelled      │                      │

**優點:**簡單、鬆散耦合、無中央協調器 缺點: 難以追蹤整體流程,可能存在循環依賴

2.2 基於編排的 Saga

Saga Orchestrator 協調整個流程:

                    ┌─────────────────┐
                    │ Order Saga      │
                    │ Orchestrator    │
                    └──┬──┬──┬───────┘
                       │  │  │
            ┌──────────┘  │  └──────────┐
            ▼             ▼             ▼
      ┌──────────┐ ┌──────────┐ ┌──────────┐
      │ Payment  │ │Inventory │ │ Shipping │
      │ Service  │ │ Service  │ │ Service  │
      └──────────┘ └──────────┘ └──────────┘

Orchestrator:
1. Create Order (PENDING)
2. Command: ProcessPayment → Payment Service
3. If success: Command: ReserveInventory → Inventory Service
4. If success: Command: CreateShipment → Shipping Service
5. If any fail: Compensate previous steps in reverse

**優點:**流程清晰,易於理解,集中錯誤處理 缺點: Orchestrator 可能成為瓶頸、單點故障

2.3 什麼時候使用什麼?

標準編舞編排
複雜性2-3 步驟4+ 步驟
可見性難以追蹤清除
聯軸器寬鬆媒介(到協調器)
錯誤處理分散式集中式

3. 補償交易

補償事務=業務事務的「撤銷」(不是資料庫回滾):

Forward:                  Compensating:
CreateOrder         →     CancelOrder
ProcessPayment      →     RefundPayment
ReserveInventory    →     ReleaseInventory
CreateShipment      →     CancelShipment

注意:補償事務必須是冪等的-多次呼叫會得到相同的結果。


4. 冪等性-處理重複訊息

在分散式系統中,訊息可以發送多次(至少一次傳遞)。服務必須處理重複項:

// Idempotent payment processing
async function processPayment(command) {
  // Check idempotency key
  const existing = await db.findPayment(command.idempotencyKey);
  if (existing) return existing; // Already processed
  
  // Process payment
  const result = await stripe.charge(command.amount);
  await db.savePayment({
    idempotencyKey: command.idempotencyKey,
    ...result
  });
  return result;
}

5. 實務:電子商務訂購傳奇

Create Order Saga (Orchestration):

Step 1: Order Service   → CreateOrder(PENDING)
Step 2: Payment Service → ChargeCustomer(orderId, amount)
  ✅ → Step 3
  ❌ → Compensate: CancelOrder → DONE (Order: PAYMENT_FAILED)

Step 3: Inventory Service → ReserveItems(orderId, items)
  ✅ → Step 4
  ❌ → Compensate: RefundPayment → CancelOrder → DONE (Order: OUT_OF_STOCK)

Step 4: Order Service → ConfirmOrder(orderId)
  ✅ → DONE (Order: CONFIRMED)
  
Step 5 (async): Notification Service → SendConfirmationEmail

總結

  • 2PC 不適合微服務 — 太慢且脆弱
  • Saga Pattern = 本地事務序列 + 補償事務
  • 編排:簡單流程(2-3步),鬆散耦合
  • 編排:複雜的流程(4+步驟),清晰的可見性
  • 冪等性是必要的-始終處理重複的訊息

下一篇文章: 第 9 課:事件溯源與 CQRS — 何時需要,何時不需要?