設計數位錢包 (Digital Wallet)

Hard★★★★★Draft

FromSystem Design Interview

Concepts practiced🤝 分散式交易與兩階段提交 (Distributed Transactions & 2PC)🤝 共識 (Consensus)🛡️ 冪等設計 (Idempotency)

Convention: read the requirements & constraints first, sketch your own design, then expand the reference to compare.

📚 From the Books

設計數位錢包 (Digital Wallet)ProblemHardFintech

支付平台上兩個錢包之間的直接餘額轉帳:更快、通常免手續費。難點是在 100 萬 TPS 下保證跨帳戶轉帳的正確性,還要能事後重建每一分錢的歷史。

📋 Requirements

🧠 Core Ideas

TIP

設計沿著三代方案演進:記憶體分片 → 分散式交易 → 事件溯源。核心目標之一是提升單一節點能處理的 TPS:每節點越快,達到百萬 TPS 所需的節點數越少(1,000/節點需 2,000 個節點,10,000/節點只需 200 個),硬體成本越低。

先看最直覺的記憶體分片:帳戶餘額是 <user, balance> map,用 Redis 叢集分片(對 account ID 取 hash 再除以分區數),分片資訊放 Zookeeper,無狀態的錢包服務定位節點後更新兩邊餘額。問題是:一筆轉帳要更新兩個 Redis 節點,無法保證兩次都成功——若服務在第一次更新後、第二次前當機,就是不完整的轉帳。這兩次更新必須在單一原子交易中完成。

⚖️ Reference Design

分散式交易 — 2PC / TC-C / Saga

把 Redis 換成交易型關聯式資料庫後,跨庫的兩個更新用分散式交易做到原子性:

  • 2PC(低階):協調者(錢包服務)先要各庫 prepare,全 yes 才 commit,任一 no 就 abort。缺點是等待期間鎖持有很久、效能差,且協調者是單點故障
  • TC-C(高階,補償式):兩階段各自是獨立交易——Try 保留資源,再依結果 Confirm 或 Cancel。補償(undo)寫在業務邏輯裡,與資料庫無關,但複雜度落到應用層。轉帳的有效順序只有「先從 A 扣款、C 做 NOP」;Try 階段結束會出現帳戶總和暫時少 1 美元的不平衡狀態,這對應用是可見且可控的。
  • Saga:所有操作排成線性序列,依序執行,失敗則反向用補償交易回滾;協調用編舞(去中心化)或編排(單一協調者),數位錢包偏好編排。

TC-C 操作可並行、Saga 只能線性;對延遲敏感且服務多選 TC-C,想跟微服務趨勢選 Saga。用 phase status 表(存在被扣款帳戶那個庫)記錄進度,才能在協調者重啟後恢復;並用亂序旗標處理 Cancel 比 Try 先到的情況。

事件溯源 — 可稽核與可重現

分散式交易能轉帳,但只存「更新後的餘額」,難以追溯為何改變、歷史餘額也遺失了。事件溯源用四個概念解決:Command(外界意圖,放 FIFO 佇列如 Kafka)、Event(驗證後的既成事實,過去式、確定性)、State(帳戶餘額 map)、State machine(驗證 command 產生 event、套用 event 更新 state;行為必須確定性、不含隨機或 I/O)。

所有變更先以不可變歷史存下,資料庫只是最新檢視。因為 event 列表不可變、state machine 確定性,隨時可從頭重播 events 重建任一時間點的歷史餘額——這回答了稽核員的三問(任意時點餘額、餘額是否正確、改版後邏輯是否仍正確)。CQRS 進一步只發布 events:一個寫入用的 state machine,加上多個唯讀 state machine 各自建構查詢檢視,最終一致。

高效能、可靠與可擴展
  • 高效能:把 command/event/state 從遠端 Kafka 與 DB 改存本地磁碟——event 列表僅可附加、循序寫入極快,用 mmap 同時落盤與記憶體快取;state 用 SQLite 或 RocksDB(LSM tree,寫入優化)。定期拍 snapshot 存物件儲存(如 HDFS),重播時從 snapshot 續跑而非從頭。
  • 可靠性:只有資料需要可靠(運算可在他處重跑),而四類資料中 state 與 snapshot 都能由 event 重建、command 又不保證確定性,故只有 event 列表需要強可靠性。用 Raft 共識把 event 列表複製到多節點:多數在線即可運作(3 節點容忍 1 壞、5 節點容忍 2 壞),leader 收 command 轉 event 並複製給 follower,leader 當機自動重選。
  • 可擴展:分片成多個 Raft 群組,群組間再用 TC-C 或 Saga 做分散式交易;加反向代理讓唯讀 state machine 一收到 event 就 push 狀態,把非同步框架對外變成同步回應。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading