TCC Pattern

部分內容由 LLM 生成,尚未經過人工驗證。

TCC(Try-Confirm-Cancel)是一種分散式事務模式,將操作拆成三個階段,以實現最終一致性並降低鎖競爭。

核心邏輯:從「扣錢」變成「凍結」

在分散式系統中,我們不能直接扣錢,因為萬一對方收不到錢,這 100 元就消失了。TCC 將這件事拆成三個本地事務:

  • Try (凍結): 檢查 A 是否有 100 元,有的話就將這 100 元從 可用餘額 轉入 凍結餘額
  • Confirm (扣除): A 帳戶真正扣掉那 100 元 凍結餘額;B 帳戶增加 100 元 可用餘額
  • Cancel (解凍): 如果轉帳失敗,將 A 帳戶的 100 元從 凍結餘額 退回 可用餘額

帳戶資料表結構

為了支援 TCC,帳戶表通常會設計成這樣:

CREATE TABLE account (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL,
    balance DECIMAL(18,2),           -- 總額
    available_balance DECIMAL(18,2), -- 可用餘額 (可消費的錢)
    frozen_balance DECIMAL(18,2),    -- 凍結餘額 (處理中的交易)
    updated_at TIMESTAMP
);
欄位說明
balance總額
available_balance可用餘額(可消費的錢)
frozen_balance凍結餘額(處理中的交易)

三個階段的操作細節

以 A 轉帳 100 元給 B 為例:

階段銀行 A(出帳方)銀行 B(入帳方)
Try凍結資源可用-100凍結+100預留位置:檢查帳號是否存在,狀態是否正常。
Confirm正式扣款凍結-100總額-100正式入帳可用+100總額+100
Cancel解凍資金凍結-100可用+100釋放狀態:取消此次交易入帳權限。

序列圖:金融轉帳 TCC 流程

  sequenceDiagram
    autonumber
    participant TM as 事務管理器 (Coordinator)
    participant S_Acc as 銀行A (出帳服務)
    participant R_Acc as 銀行B (入帳服務)

    Note over TM, R_Acc: --- 階段一:Try (資金凍結) ---
    TM->>S_Acc: Try: 帳戶 A 餘額充足?凍結 $100
    S_Acc-->>TM: 凍結成功 (A錢包-$100, 凍結+$100)
    TM->>R_Acc: Try: 帳戶 B 狀態正常?預留額度
    R_Acc-->>TM: 檢查成功 (B帳號可接收款項)

    alt 雙方 Try 成功
        Note over TM, R_Acc: --- 階段二:Confirm (真正入帳) ---
        TM->>S_Acc: Confirm: 真正扣除 A 凍結的 $100
        S_Acc-->>TM: 扣款成功 (總額減少)
        TM->>R_Acc: Confirm: 真正增加 B 的可用餘額 $100
        R_Acc-->>TM: 入帳成功 (總額增加)
    else 任何一方失敗 (例如 B 帳戶已被註銷)
        Note over TM, R_Acc: --- 階段三:Cancel (資金解凍) ---
        TM->>S_Acc: Cancel: 解凍 A 凍結的 $100
        S_Acc-->>TM: 解凍成功 (凍結-$100, 可用+$100)
        TM->>R_Acc: Cancel: 取消 B 的預留狀態
        R_Acc-->>TM: 取消成功
    end

關鍵筆記

為什麼要「凍結」?

如果直接在 Try 階段扣 A 的錢(不留痕跡),萬一 B 入帳失敗要 Cancel,A 可能會覺得很奇怪:「我的錢怎麼突然少了一小時又回來了?」凍結則讓 A 知道這 100 元正在「處理中」,這才是正確的用戶體驗。

冪等性(Idempotency)是生命線

在金融系統中,網路可能會超時。如果 Confirm 請求發了兩次,銀行 B 絕對不能幫用戶加兩次錢($100 + $100)。必須透過「交易流水 ID (Transaction ID)」來保證同一個事務只會執行一次 Confirm。

鎖顆粒度

傳統 2PC 鎖的是資料庫整行;TCC 鎖的是 frozen_balance 這個欄位數值。這意味著:在轉帳 100 元的過程中,帳戶 A 剩下的錢還是可以進行其他消費的。這就是 「鎖顆粒度變小」 帶來的性能紅利。


與其他模式比較

維度TCC PatternOutbox Pattern2PC
設計目標最終一致性 + 低鎖競爭訊息可靠投遞強一致性
鎖機制業務層凍結(細粒度)依賴 DB Row Lock全域鎖
回滾機制Cancel 補償無(At-Least-Once)Rollback
適用場景金融轉帳、庫存扣減事件驅動架構單一資料庫

適用場景

  • 金融轉帳:跨行轉帳、支付系統
  • 庫存扣減:電商下單、秒殺活動
  • 積分兌換:跨服務積分操作
  • 預約系統:機票、飯店預訂

相關主題