搜尋文件

輸入關鍵字,搜尋標題與正文。

    分散式系統文件 01/08

    分散式系統、部分失敗與故障模型

    本文提要

    分散式系統由透過訊息交換協作的程序組成,一個程序的成功與失敗,無法直接代表整項工作的結果。 理解它的起點,是把「遠端發生了什麼」和「我目前知道什麼」分開。

    18px · 寬鬆行距

    閱讀設定

    排版預覽 · 示範文字(可捲動)

    檔案與目錄

    Linux 以一棵從根目錄 / 開始的樹狀結構組織檔案。目錄用來整理檔案,也可以包含其他目錄。

    pwd
    ls -la /home
    18px

    16、18、20、22px;隨瀏覽器文字大小縮放。

    欄寬套用於背景文章;此處預覽字級與行距。手機正文自動配合螢幕。

    文件與本文目錄
    本主題文件

    分散式系統由透過訊息交換協作的程序組成,一個程序的成功與失敗,無法直接代表整項工作的結果。 理解它的起點,是把「遠端發生了什麼」和「我目前知道什麼」分開。

    本文是系列第一篇,先建立後續討論重試、一致性與共識所需的模型。若對請求與回應尚不熟悉,可先閱讀輸入、輸出與失敗語意。

    查證日期:2026-11-04。參考文末論文與官方文件;本文未執行分散式服務或故障實驗。文章匯出服務與全部事件歷程均為自創概念例子。

    從一個匯出工作開始

    假設使用者要求把一組文章匯出成檔案。最初的服務只有呼叫端、處理請求的 API 程序與一個資料庫:API 建立工作資料列,提交後回傳工作識別,稍後由背景工作產生檔案。

    這裡已經有多個各自執行、透過通訊協作的參與者。即使 API 與資料庫部署在同一台主機,它們之間也可能有程序停止、等待與回應中斷的邊界;分散式問題不由機器數或是否採用微服務來判定。Lamport 的模型以程序、事件與訊息交換描述系統,提供不依賴特定部署形式的思考起點。參考 Lamport(1978),Introduction 與 The Partial Ordering。

    本例把「建立工作」與「檔案完成」分開。拿到工作識別只證明建立操作達到契約規定的狀態,不能直接宣稱背景工作成功;對外表示方式見長時間執行操作與非同步 API 契約。

    同一個逾時,可以對應三種不同歷程

    呼叫端送出建立請求後,等到期限仍沒拿到回應。下表三種歷程,在呼叫端都可能只留下相同的「等待逾時」。

    表格・4 欄 3 列
    歷程遠端實際發生的事工作是否存在呼叫端能直接確定什麼
    A:請求未送達連線中斷,API 未處理該請求本次請求沒有建立工作尚未取得結果
    B:提交前停止API 收到請求,但資料庫交易沒有提交就終止本例假設未提交交易會回復,因此沒有工作尚未取得結果
    C:提交後失去回應工作已持久提交,API 在回應前停止,或回應在途中遺失有工作尚未取得結果

    表格是依本例條件推演,不是在列舉所有實作的錯誤碼。若呼叫端沒有其他證據,它無法從相同的逾時觀測辨認 A、B、C;因此下一步若直接建立新工作,C 就可能造成重複意圖。

    通訊成功的確認也有層次。TCP 處理的是位元組流,不能替應用證明某筆交易已提交,更不能證明檔案已產生;相關傳輸責任見 TCP 基礎。gRPC 也明確指出,兩端可能各自判定呼叫結果:伺服器成功完成,呼叫端仍可能因期限到達而得到失敗。參考 gRPC Core concepts:RPC termination。

    部分失敗讓系統同時處在不同狀態

    部分失敗(partial failure)表示不能把整套系統視為同時成功或同時失敗的單一單位。API 可以仍然接受健康檢查,卻無法連到資料庫;某個呼叫端無法到達 API,其他呼叫端仍正常;worker 完成輸出後,通知服務可以暫時不可用。AWS 的故障處理說明也以部分與暫時故障作為重試策略的背景。參考 AWS:Timeouts, retries, and backoff with jitter。

    這帶來兩個分開的問題:實際狀態是否符合需求,以及參與者是否有足夠資訊知道該狀態。增加監控可以提供證據,但健康檢查成功也不能代替查證某一個匯出工作。

    網路分割(network partition)是某些參與者之間無法交換所需訊息;不要求所有機器都離線。它也可能是不對稱的,或只有一條服務間路徑受影響。觀察不到對方時,應用不能只憑沉默推斷對方已永久停止;下節的時間假設決定了這項推斷能有多強。

    先說明允許哪些故障

    故障模型(failure model)不是對真實世界所有問題的完整描述,而是這次推理允許發生哪些情況。模型外的故障,不能自動套用模型內的保證。

    表格・3 欄 4 列
    模型或條件允許的行為寫作與設計時要追問的事
    Crash-stop程序停止,之後不再參與其他節點如何繼續?
    Crash-recovery程序停止後可以重啟哪些狀態持久保存?哪些記憶體狀態會消失?
    訊息延遲、遺失或重複訊息可能晚到、沒到,重送也可能重複抵達是否有識別、確認與重送規則?
    Byzantine failure程序可能任意偏離協定,包含傳遞矛盾訊息演算法是否真的支援此模型?

    本系列的工程例子主要考慮程序停止與恢復、通訊異常及過載;不假設程序會惡意偽造協定狀態。這是範圍選擇,不是安全防護。Raft 等非 Byzantine 協定不能因此推廣到任意惡意節點;其假設可對照 Raft 論文 §2、§5。

    說「已持久保存」也必須有儲存條件。例如本例假設已確認提交的資料能在 API 程序重啟後保留;若磁碟本身毀損或整個資料庫站點消失,需要額外的複寫、備份與恢復設計,不能由 API 成功回應推出。

    時間假設決定沉默代表什麼

    在同步模型(synchronous model)中,推理依賴已知的訊息延遲與程序執行時間上界;非同步模型(asynchronous model)不提供這種可依賴的有限上界。程序很慢與程序已停止,可能長時間無法區分。

    部分同步(partial synchrony)則描述介於兩者之間的條件,例如系統最終進入有界延遲的期間,但事先不知道何時開始。它不是「大部分請求很快」的統計說法,而是演算法成立所需的時序條件。參考 Dwork、Lynch、Stockmeyer(1988),Consensus in the Presence of Partial Synchrony。

    工程上的 timeout 可以觸發懷疑、切換或重試,卻不會把不具已知上界的網路變成同步模型。系統可能選擇在等待過久後採取行動,但仍要處理原節點其實只是暫停、稍後恢復的情況。

    安全性與活性回答不同問題

    安全性(safety)關心不允許的事情不會發生;活性(liveness)關心要求的進展最終能發生。這裡的 safety 是演算法正確性用語,不是 Web 資安的同義詞。兩者是分散式規格中常見的區分,參考 Lamport:Proving the Correctness of Multiprocess Programs。

    以本例自訂規格來說:

    • 安全性:「在約定保留期與作用範圍內,同一匯出意圖最多對應一個工作。」
    • 活性:「在服務與依賴恢復、重試持續且資源足夠的條件下,已接受的有效意圖最終能取得工作結果或明確終止狀態。」

    永遠拒絕所有建立請求,可能避免建立重複工作,卻無法提供需要的進展。遇到逾時就無條件建立新工作,可能讓使用者更快拿到某個結果,卻破壞上述安全性。提出設計時,兩項都要說明,且活性要附上恢復、排程與資源等前提。

    第一篇留下的問題是:既然沉默無法證明原操作沒做,服務要如何讓同一意圖可以安全重送?下一篇會把視角從對外契約移到冪等副作用的原子邊界。

    相關文件