具代表性的面試主題

系統設計面試:為 SaaS 設計預付使用額度帳本

系統設計困難
Offer.cc 編輯團隊發佈 更新

題幹

一個資料增強 SaaS 銷售預付額度。請設計服務來發放額度、授權不同成本的操作、處理重試與退款,並在至少一次投遞的使用事件下保持可稽核。

題目與範圍

假設有 2 萬個租戶、每小時 100 萬次操作,短時突發可達 10 倍,授權決策 p95 低於 100 毫秒。客戶先購買額度,再執行操作。每次操作成本可能不同,請求可以並發到達,使用事件至少投遞一次。設計必須防止餘額變負和重複扣費,並保留稽核軌跡。這是購買所得價值的帳本,不是限流器,也不是週期訂閱計費系統。

面試官考察什麼

  • 區分不可變額度流水與快速餘額讀模型。
  • 讓授權、扣取、釋放、退款和發放操作具備冪等性。
  • 處理並發請求、延遲事件、重試、過期和對帳。
  • 說明一致性邊界、熱點租戶、可觀測性與恢復方案。

可以先問的釐清問題

確認額度是否過期、失敗操作是否先預留再釋放、退款由誰審批、成本是否在執行前確定,以及一個租戶是否會在多個區域消費。再確認決策延遲要求和是否允許臨時超額授權。

30 秒回答

我會使用由發放、預留、扣取、釋放、退款和過期組成的追加寫入不可變帳本。事務內維護餘額投影以支援快速讀取,用冪等鍵讓每次變更可重複執行。授權時原子檢查可用額度並建立帶過期時間的預留;成功後扣取,失敗後釋放;回收器處理遺留預留。透過 outbox、可重播事件和對帳任務比較投影與帳本,並用可稽核的補償流水修復偏差。

分步深入回答

1. 建模額度狀態

按租戶或帳戶保存整數額度單位、帳本序號,以及發放、預留、扣取、釋放、退款、過期總額的投影。每筆變更記錄 operation_id、冪等鍵、原因、操作者和時間。舊流水不修改,糾正只能追加補償流水。

2. 在不重複消費的前提下授權

對租戶熱點鍵使用同一事務或線性一致的條件更新:只有可用額度不少於本次成本時才建立預留。重複冪等鍵返回原決定;同一鍵攜帶不同參數則拒絕。預留保存過期時間,扣取和釋放都要帶狀態條件,不能同時成功。

3. 連接業務執行與事件

業務請求攜帶預留識別碼。確認成功後扣取,失敗或取消後釋放。使用事件可能重複或延遲,消費者按 event_id 去重,對未知預留執行對帳,不能直接減少計數器。outbox 在事務提交後發布帳本變更。

4. 擴展、替代方案與恢復

按租戶分區,讓同一租戶路由到固定寫入區域,並用准入控制或串行命令流隔離極熱租戶。租戶寫入量中等時,關聯式資料庫事務更簡單;極熱租戶更需要事件溯源命令流,以順序和可重播性換取查詢複雜度。只有能接受延遲的讀取才使用副本,並展示資料時間。清掃器使遺留預留過期,重播帳本可重建投影。監控負可用額度、過期後扣取、重播延遲和對帳差異。

高品質示例回答

我會先確認過期、退款權限、多區域消費,以及成本是否在執行前已知。不可變的每租戶帳本是真實來源,餘額表只是可重建投影。authorize 在冪等鍵和過期時間約束下原子預留額度,成功後 capture,失敗後 release 或等待過期。重複或延遲事件先去重再對帳。嚴格禁止重複消費時採用租戶歸屬寫入區域;若必須多區域獨立消費,則分配明確的區域預算並顯式對帳債務,說明最大臨時超額。所有糾正都追加補償流水。

常見失誤

  • 只有可變計數器 → 重試和退款無法稽核 → 追加帶操作識別碼的不可變流水。
  • 請求到達就扣費 → 失敗操作也消耗額度 → 先預留,成功後才扣取。
  • 把重試當作新消費 → 同一操作被扣兩次 → 重用同一冪等鍵。
  • 預留從不回收 → 崩潰工作程序永久鎖住額度 → 增加過期時間和條件清掃器。
  • 用最終一致的跨區讀取 → 兩個寫入者可能超額消費 → 單寫入者或明確區域預算。
  • 修改舊行修復歷史 → 稽核軌跡失去可信度 → 追加補償流水。

追問與回答

用戶端在授權後逾時,怎麼辦?

先查詢預留,或使用同一冪等鍵重試下一狀態。只有確認原預留已扣取、釋放或過期後,才建立新的授權。

月結後才發現需要退款,怎麼辦?

追加一筆引用原扣取的退款流水,並記錄審批資訊。投影增加可用額度,帳本保留兩筆事件及其順序。

兩個區域能同時消費同一帳戶嗎?

嚴格防止重複消費時,優先使用單寫入者或租戶歸屬區域。若必須本地消費,則分配明確區域預算並對帳債務,同時說明臨時超額上限。

如何證明餘額正確?

定期重播帳本,比較推導出的可用額度與投影,並把扣取流水與業務結果比較。每次修復寫入冪等補償流水和稽核記錄。

公開來源

同類題目

相關面試工具

用 Solve 整理系統設計回答

從澄清需求開始,展開規模、架構、元件選擇和取捨。

查看工具