架構面試題 #1, 線上交易的正確性
摘要提示
- 面試目標: 以實作型應用題觀察候選人對微服務與雲原生思維的問題拆解與實作能力
- 交易正確性: 以「金錢守恆」為原則,避免任何一邊扣款另一邊未入帳的半套交易
- 驗證方法: 以多執行緒壓測與單元測試驗證 N×M 次轉帳後餘額是否精準符合預期
- 單機解法: 透過 lock/Interlocked 建立 Critical Section,消除 Race Condition
- DB 交易解法: 利用 RDBMS 的 ACID/Transaction,將交易邏輯下沉到資料庫側
- 規模限制: 單一資料庫連線與擴展能力限制了服務在高併發下的可擴充性
- 修正觀點: RDBMS 與 NoSQL 各擅所長,依 CAP/ACID/BASE 與資料型態選擇技術
- 分散式鎖: 在多實例環境以 Redis + RedLock 建立分散式 Critical Section
- 可靠性細節: 鎖需具備到期、重試、原子 Compare-and-Swap 等確保一致性
- 面試應用: 由小到大三解法,對應 Junior/Senior/Architect 的思維與實作深度
全文重點
作者以微服務團隊招募為背景,提出一組「白板應用題」來辨識工程師是否具備以雲原生角度解題與落地的能力。核心題目聚焦於「線上交易的正確性」,強調金錢守恆:在封閉系統中金錢不得憑空產生或消失,也不可允許半套交易。驗證方式是以多執行緒/多程序重複小額入帳,測試最終餘額是否精準一致。
文中依系統規模遞進給出三種典型解法。解法一(單機):以 C# lock/Interlocked 將「寫交易紀錄+異動帳戶餘額」包成 Critical Section,消除 Race Condition,單機即可保證一致性。若省略鎖,將產生約兩成的金額流失,清楚揭示多執行緒讀寫交錯的風險。
解法二(資料庫交易):在 RDBMS(以 SQL Server 示範)以 Transaction 包裹 insert+update+select,將 ACID 下沉至 DBMS。此路徑實作簡單、語意清晰,易於保證一致性,但擴展性受單一資料庫連線/處理量限制。作者亦補充 2021 年修正:RDBMS 與 NoSQL 並非二選一,應依 CAP/ACID/BASE 與資料模型與查詢/交易需求取捨;在微服務場景,不同服務選用合適資料庫是常態。
解法三(分散式鎖):當系統規模擴至多實例且使用不支援交易的儲存體(以 MongoDB 示範)時,需以 Redis+RedLock 建立跨實例的分散式鎖,將「讀取、更新、寫回」包在鎖內,並處理鎖的等待、重試與到期釋放,以避免死鎖與長期占用。作者以 Docker 快速起服務,使用多程序平行壓測,證實即便無 DB 交易,仍可藉分散式鎖精準守護金錢守恆。
文末總結:好的面試題應抽象核心問題、分層拆解並對應不同規模;工程師要能以 ACID、CAP/BASE、鎖與併發控制等基礎支撐實作;招募與自我精進都應回歸「選擇合適的架構與工具」的原則,並以可驗證的方法證明方案可靠。
段落重點
前言與導讀
作者拋開標準答案型的系統設計考題,改用應用題考察候選人在微服務/雲原生環境下的思考與落地能力。核心是觀察面試者能否先抽象化問題,再依不同規模(單機、負載均衡、多服務)與不同資料儲存(SQL/NoSQL)提出可行解與取捨。題目涵蓋交易正確性、演算法、巨量資料、API 設計與 OOP 等,強調沒有唯一解,重點在於推理過程與邏輯完備。
考題:線上交易的正確性
以 C# 抽象類別 AccountBase 為骨架,要求實作 GetBalance/ExecTransaction,並以多執行緒壓測驗證 N×M 次入帳後餘額精準一致。強調金錢守恆定律:封閉系統內金額總量不變,不得出現只扣未入或只入未扣的半套交易;暫不討論 rollback 與 crash,只驗證正常交易路徑的正確性。以單元測試和性能觀察為驗證手段,聚焦在「如何避免併發導致的錯帳」。
解法1:單機運作(Lock 與 Critical Section)
在單機情境,以 lock 將「寫交易歷史+調整餘額」包成 Critical Section,避免併發讀寫導致 Race Condition。示範若移除 lock,壓測下會少掉約 20% 金額;加上 lock 則精準一致。核心關鍵字包括 Lock、Critical Section、Race Condition;本質是以互斥保護不可分割的讀改寫序列,直到完成再釋放。此法簡潔高效,適用單機或同程序內的多執行緒。
解法2:搭配 SQL Transaction(RDBMS 的 ACID)
將交易邏輯下沉至 SQL Transaction,透過 begin tran/commit 將 insert+update+select 原子化,應用 Dapper 範例展示簡潔實作。優點是易用、語意清晰、一致性強;限制在於單一資料庫的連線與擴展瓶頸。作者於 2021/03/06 增補觀點:RDBMS 著重 ACID(對應 CAP 的 CA),NoSQL 著重可用與彈性(常見 BASE/AP);在微服務中常見多元資料庫並存,應按資料型態、查詢模式與一致性需求選擇技術組合。
解法3:不支援交易的儲存體(分散式鎖定)
當採用不支援交易的 NoSQL(以 MongoDB 示範)且服務多實例時,需以分散式鎖(Redis+RedLock)打造跨實例的 Critical Section。程式在進入讀改寫前嘗試取得鎖,設置到期時間、等待與重試策略,完成後釋放,以避免鎖遺失與飢餓。作者以 Docker 快速建 Redis/Mongo,透過 10 個 process × 20 執行緒並發壓測 20 萬筆交易,驗證最終餘額精準無誤。並提醒分散式鎖的可靠性仰賴原子操作(如 CAS)、到期機制與故障情境考量,建議優先使用成熟套件。
總結
本題以三階段解法對應不同職級與系統規模:單機鎖、RDBMS 交易、分散式鎖。關鍵能力在於抽象化問題、辨識一致性與效能/擴展性的取捨、並以可驗證手段證明正確性。面試官可因應對象深度調整追問與延伸;求職者則應紮實掌握併發控制、ACID/CAP/BASE 與資料庫選型,並具備從單元測試到壓測的驗證能力。文末提供程式碼連結,鼓勵讀者親手實作、以實證支撐架構選擇。
資訊整理
知識架構圖
- 前置知識
- 併發與多執行緒基礎(Thread、Critical Section、Race Condition)
- 基本鎖定機制(語言層 lock/monitor、Interlocked/CAS 概念)
- 資料庫基礎(RDBMS、ACID、Transaction、Isolation Level)
- 分散式系統基礎(CAP、BASE、分散式鎖)
- 測試觀念(單元測試、壓力測試、可重現驗證)
- 核心概念(3-5 個)
- 金錢守恆與一致性:在封閉系統中交易不可「只完成一半」,總額守恆
- 臨界區與競態條件:以鎖保護讀-改-寫,避免多執行緒覆寫結果
- ACID 與資料庫交易:讓 DBMS 在單庫範圍內保證一致性
- 分散式鎖與微服務擴展:跨多實例以共享資源(如 Redis)協調臨界區
- 驗證策略:以多執行緒/多進程測試,對比預期與實際餘額
- 技術依賴
- 單機方案:語言/OS 鎖(lock/monitor、Interlocked/CAS)→ 受限單進程
- 單庫方案:RDBMS Transaction(例如 SQL Server、Dapper/TransactionScope)→ 受限單一 DB 範圍
- 分散式方案:共享快速存儲(Redis)+ 分散式鎖演算法(RedLock)→ 可橫向擴展,多實例協調
- 儲存層選擇:RDBMS(ACID)/ NoSQL(CAP 的 AP/BASE)→ 依需求取捨
- 測試與觀測:多執行緒/多進程驅動 + 交易明細/餘額查核 + 效能度量
- 應用場景
- 金融/帳務/錢包/遊戲幣交易正確性
- 微服務環境中的跨實例一致性控制
- 在不支援交易的儲存體(如舊版 MongoDB)上保證一致性
- 面試白板題/內部訓練/POC 驗證併發正確性與擴展能力
學習路徑建議
- 入門者路徑
- 了解競態條件與臨界區,能用語言內建 lock/monitor 實作
- 用簡單的多執行緒測試(N 執行緒 × M 次加 1),驗證有無鎖的差異
- 熟悉單元測試與簡單效能量測(吞吐量、預期/實際對比)
- 進階者路徑
- 掌握 RDBMS 交易(ACID、Isolation Level、死鎖與重試)
- 以 SQL Transaction/TransactionScope 包交易,理解其邊界(單庫)
- 了解 CAP/BASE 與 RDBMS/NoSQL 的取捨,能挑選適合技術棧
- 實戰路徑
- 在微服務/多實例中導入分散式鎖(Redis + RedLock 或等效方案)
- 為不支援交易的儲存體設計臨界區(鎖定資源命名、過期、重試策略)
- 以多進程/多容器實測一致性與吞吐,補足失敗/超時/重試與告警
- 延伸工程化:冪等性、重入設計、觀測與追蹤(交易序號、審計日誌)
關鍵要點清單
- 金錢守恆定律: 在封閉系統內交易不可半途而廢,總金額應守恆不變(除非外部流入/流出) (優先級: 高)
- 競態條件(Race Condition): 多執行緒的讀-改-寫交錯會覆蓋結果,導致金額錯誤 (優先級: 高)
- 臨界區(Critical Section): 將不可分割的多步驟操作以鎖包裹,確保原子性 (優先級: 高)
- 語言層鎖定(lock/monitor/Interlocked): 單機/單進程中最直接的正確性保障 (優先級: 高)
- ACID 與資料庫交易: 在單一資料庫範圍內由 DBMS 保證一致性與隔離性 (優先級: 高)
- 交易邊界設計: 明確界定一筆交易的開始/結束與涉及的資源,避免半完成狀態 (優先級: 中)
- 分散式鎖(Redis/RedLock): 跨實例協調臨界區,支援等待、重試及過期回收 (優先級: 高)
- CAP 與 BASE 心智模型: 依需求在一致性、可用性、分區容錯間做取捨 (優先級: 中)
- NoSQL 與一致性策略: 在缺乏原生交易的儲存體上以應用層與鎖補足一致性 (優先級: 中)
- 測試與驗證方法: 多執行緒/多進程壓測,對比預期與實際餘額並量測吞吐 (優先級: 高)
- 重試與過期策略: 分散式鎖需設計重試間隔與過期時間,避免鎖洩漏與活鎖 (優先級: 高)
- 比較交換(CAS)概念: 鎖與原子操作的底層基礎,理解可提升方案正確性 (優先級: 中)
- 資料模型與審計: 同步維護餘額與交易明細,便於對帳與稽核 (優先級: 中)
- 擴展與瓶頸辨識: 單庫交易的連線/鎖競爭上限、分散式鎖的可用性與可靠性 (優先級: 中)
- 面試應用與表達: 以抽象化問題→多規模解法→邊界與風險,系統化呈現思路 (優先級: 低)