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 Pattern | Outbox Pattern | 2PC |
|---|---|---|---|
| 設計目標 | 最終一致性 + 低鎖競爭 | 訊息可靠投遞 | 強一致性 |
| 鎖機制 | 業務層凍結(細粒度) | 依賴 DB Row Lock | 全域鎖 |
| 回滾機制 | Cancel 補償 | 無(At-Least-Once) | Rollback |
| 適用場景 | 金融轉帳、庫存扣減 | 事件驅動架構 | 單一資料庫 |
適用場景
- 金融轉帳:跨行轉帳、支付系統
- 庫存扣減:電商下單、秒殺活動
- 積分兌換:跨服務積分操作
- 預約系統:機票、飯店預訂