分散式系統的挑戰
前言
當一台機器不夠用時,問題才真正開始。 分散式系統是現代網際網路的基石——從微信訊息到淘寶下單,背後都是成百上千台機器協同工作。但「分散式」不是免費的午餐,它帶來了一系列單機系統從未遇到的挑戰。
這篇文章會帶你學什麼?
學完這章後,你將獲得:
- 核心定理:理解 CAP 定理及其對系統設計的影響
- 一致性模型:區分強一致性、最終一致性、因果一致性
- 八大挑戰:掌握分散式系統面臨的核心難題
- 共識演算法:了解 Paxos、Raft 等分散式共識的基本思想
- 實戰模式:熟悉 2PC、Saga、CRDT 等常用解決方案
| 章節 | 內容 | 核心概念 |
|---|---|---|
| 第 1 章 | 為什麼需要分散式 | 擴展性、可用性、地理分佈 |
| 第 2 章 | CAP 定理 | 一致性、可用性、分區容錯 |
| 第 3 章 | 一致性模型 | 強一致、最終一致、因果一致 |
| 第 4 章 | 八大挑戰 | 網路、時鐘、分區、腦裂等 |
| 第 5 章 | 共識演算法 | Paxos、Raft、ZAB |
| 第 6 章 | 分散式事務 | 2PC、Saga、TCC |
0. 全景圖:為什麼需要分散式系統?
單機系統簡單可靠,但有三個無法逾越的瓶頸:
| 瓶頸 | 說明 | 分散式的解法 |
|---|---|---|
| 效能上限 | 單機 CPU、記憶體、磁碟有物理極限 | 水平擴展:加更多機器分擔負載 |
| 單點故障 | 一台機器掛了,整個服務就掛了 | 冗餘副本:多台機器互為備份 |
| 地理延遲 | 使用者在全球各地,單機只能在一個地方 | 多地部署:就近服務使用者 |
分散式的代價
分散式系統解決了上面的問題,但引入了新的複雜性:網路不可靠、時鐘不同步、部分失敗、資料一致性……這些就是本文要討論的「挑戰」。
Peter Deutsch 的分散式計算八大謬誤告訴我們,以下假設在分散式環境中都是錯的:
- 網路是可靠的
- 延遲是零
- 頻寬是無限的
- 網路是安全的
- 拓樸不會變化
- 只有一個管理員
- 傳輸成本是零
- 網路是同構的
1. CAP 定理:分散式系統的「不可能三角」
2000 年,Eric Brewer 提出了 CAP 猜想(後被證明為定理):一個分散式系統最多只能同時滿足以下三個屬性中的兩個。
| 屬性 | 含義 | 通俗理解 |
|---|---|---|
| Consistency(一致性) | 所有節點在同一時刻看到相同的資料 | 你在任何 ATM 查餘額,結果都一樣 |
| Availability(可用性) | 每個請求都能收到非錯誤的回應 | 系統永遠能回應你,不會說「服務不可用」 |
| Partition tolerance(分區容錯) | 網路分區時系統仍能繼續運行 | 即使部分網線斷了,系統還能工作 |
為什麼只能選兩個?
在分散式環境中,網路分區(P)是不可避免的——光纖會被挖斷、交換器會故障、資料中心會斷網。所以 P 是必選項,實際的選擇是在 C 和 A 之間權衡:
- 選 CP:分區時拒絕不確定的請求,保證資料正確 → 適合金融、庫存
- 選 AP:分區時繼續服務,但資料可能暫時不一致 → 適合社交、內容
CAP 不是非黑即白
現實中的系統不是簡單的「CP 或 AP」。很多系統在不同操作上做不同的選擇——比如同一個資料庫,讀操作可以是 AP(允許讀舊資料),寫操作可以是 CP(要求多數確認)。
2. 一致性模型:資料同步的「嚴格程度」
一致性不是一個開關(有或沒有),而是一個光譜。不同的一致性模型在「正確性」和「效能」之間做不同的權衡。
一致性模型對比
| 模型 | 保證 | 延遲 | 適用場景 |
|---|---|---|---|
| 強一致性 | 讀到的一定是最新寫入的值 | 高(需等待同步) | 銀行轉帳、庫存扣減 |
| 最終一致性 | 最終所有副本會一致,但中間可能讀到舊值 | 低(寫入立即回傳) | 社交動態、DNS |
| 因果一致性 | 有因果關係的操作保證順序 | 中等 | 評論回覆、協作編輯 |
| 線性一致性 | 所有操作看起來像在單機上按順序執行 | 最高 | 分散式鎖、選主 |
| 會話一致性 | 同一會話內保證讀到自己的寫入 | 低-中 | 使用者個人資料 |
「讀己之寫」一致性
最常見的實際需求是:使用者修改了自己的資料後,自己能立即看到更新(但其他使用者可以稍後看到)。這叫「Read Your Own Writes」一致性,是最終一致性的一個實用增強。
3. 八大挑戰:分散式系統的「地雷陣」
分散式系統的複雜性不是來自某一個問題,而是多個問題交織在一起。以下是最核心的八大挑戰。
- Timeouts and retries with idempotency
- Heartbeat checks to detect connection health
- Circuit breakers to pause calls after repeated failures
挑戰之間的關聯
這八大挑戰不是孤立的,它們相互關聯:
- 網路不可靠 → 導致 網路分區 → 觸發 CAP 權衡
- 時鐘不同步 → 導致 事件排序困難 → 影響 資料一致性
- 部分失敗 → 可能導致 腦裂 → 需要 共識演算法 來解決
- 資料一致性 → 需要 分散式事務 → 但事務又受 網路不可靠 影響
沒有銀彈
分散式系統沒有「完美」的解決方案,只有「合適」的權衡。理解這些挑戰的本質,才能在設計系統時做出正確的取捨。
4. 共識演算法:如何讓多台機器「達成一致」
共識演算法是分散式系統的核心——它解決的問題是:多個節點如何就某個值達成一致?即使部分節點故障或網路延遲。
4.1 Paxos
Leslie Lamport 在 1990 年提出,是第一個被嚴格證明正確的共識演算法。
| 角色 | 職責 |
|---|---|
| Proposer | 提出提案(值) |
| Acceptor | 投票接受或拒絕提案 |
| Learner | 學習最終被選定的值 |
兩階段流程:
- Prepare 階段:Proposer 發送提案編號,Acceptor 承諾不再接受更小編號的提案
- Accept 階段:Proposer 發送具體值,多數 Acceptor 接受則提案通過
Paxos 的問題
Paxos 雖然正確,但出了名的難以理解和實作。Lamport 自己的論文用了一個希臘議會的比喻,結果讓更多人困惑了。
4.2 Raft:為可理解性而生
2014 年 Diego Ongaro 提出 Raft,目標是做一個「容易理解的 Paxos」。它把共識問題分解為三個子問題:
| 子問題 | 說明 |
|---|---|
| Leader 選舉 | 叢集中選出一個 Leader,所有寫入都經過 Leader |
| 日誌複製 | Leader 將操作日誌複製到所有 Follower |
| 安全性 | 保證已提交的日誌不會被覆蓋 |
Raft 的核心流程:
- 叢集啟動時,所有節點都是 Follower
- 如果 Follower 逾時沒收到 Leader 心跳,就變成 Candidate 發起選舉
- 獲得多數票的 Candidate 成為新 Leader
- Leader 接收客戶端請求,將日誌複製到多數節點後提交
4.3 共識演算法對比
| 演算法 | 提出時間 | 可理解性 | 使用系統 |
|---|---|---|---|
| Paxos | 1990 | 困難 | Google Chubby |
| Raft | 2014 | 容易 | etcd、Consul、TiKV |
| ZAB | 2011 | 中等 | ZooKeeper |
| EPaxos | 2013 | 困難 | 學術研究為主 |
5. 分散式事務:跨節點的「全有或全無」
單機資料庫的事務靠本地鎖和日誌就能實作 ACID。但當一個業務操作涉及多個服務/資料庫時,如何保證原子性?
5.1 兩階段提交(2PC)
最經典的分散式事務協定,分為兩個階段:
| 階段 | 協調者動作 | 參與者動作 |
|---|---|---|
| Prepare | 問所有參與者「能提交嗎?」 | 執行操作但不提交,回覆 Yes/No |
| Commit | 如果全部 Yes,發送 Commit | 正式提交;如果有 No,全部回滾 |
2PC 的問題:
- 阻塞:Prepare 後如果協調者掛了,參與者會一直等待
- 單點故障:協調者是單點,掛了整個事務卡住
- 效能差:需要多次網路往返,鎖持有時間長
5.2 Saga 模式
Saga 把一個大事務拆成多個本地事務,每個本地事務有對應的補償操作。如果某一步失敗,就逆序執行補償。
電商下單的 Saga 範例:
| 步驟 | 正向操作 | 補償操作 |
|---|---|---|
| T1 | 建立訂單(待支付) | 取消訂單 |
| T2 | 扣減庫存 | 恢復庫存 |
| T3 | 扣減餘額 | 退還餘額 |
| T4 | 確認訂單(已支付) | — |
如果 T3(扣減餘額)失敗:執行 C2(恢復庫存)→ C1(取消訂單)。
兩種編排方式:
- 編排式(Choreography):每個服務監聽事件,自行決定下一步。簡單但難以追蹤全域狀態
- 協調式(Orchestration):有一個中心協調者控制流程。清晰但協調者是單點
5.3 TCC(Try-Confirm-Cancel)
TCC 是 2PC 的業務層實作,把每個操作分為三個階段:
| 階段 | 說明 | 範例(扣庫存) |
|---|---|---|
| Try | 預留資源,但不真正執行 | 凍結 10 件庫存(可用庫存 -10,凍結庫存 +10) |
| Confirm | 確認執行,消耗預留資源 | 凍結庫存 -10(真正扣減) |
| Cancel | 取消預留,釋放資源 | 凍結庫存 -10,可用庫存 +10(恢復) |
5.4 三種方案對比
| 方案 | 一致性 | 效能 | 複雜度 | 適用場景 |
|---|---|---|---|---|
| 2PC | 強一致 | 低 | 中 | 資料庫層面的跨庫事務 |
| Saga | 最終一致 | 高 | 高 | 長流程業務(訂單、物流) |
| TCC | 最終一致 | 中 | 最高 | 資金類高可靠場景 |
實際選擇建議
- 能用單庫事務就不要用分散式事務
- 大多數業務場景用 Saga + 訊息佇列就夠了
- TCC 適合對一致性要求極高的金融場景,但開發成本很高
- 2PC 適合資料庫中介軟體(如 ShardingSphere)自動處理
總結
分散式系統是現代網際網路的基礎設施,但它的複雜性遠超單機系統。理解這些挑戰不是為了「解決」它們(很多是根本性的),而是為了在設計系統時做出正確的權衡。
回顧本章的關鍵要點:
- CAP 定理:網路分區不可避免,實際選擇是在一致性和可用性之間權衡
- 一致性模型:從強一致到最終一致是一個光譜,根據業務需求選擇
- 八大挑戰:網路不可靠、時鐘不同步、網路分區、腦裂等相互關聯
- 共識演算法:Raft 是目前最實用的共識演算法,etcd/Consul 都基於它
- 分散式事務:Saga 適合大多數場景,TCC 適合金融場景,2PC 適合資料庫層
延伸閱讀
- Designing Data-Intensive Applications - Martin Kleppmann 的分散式系統經典
- The Raft Consensus Algorithm - Raft 官方可視化演示
- CAP Twelve Years Later - Brewer 對 CAP 的重新審視
- Jepsen - 分散式系統正確性測試框架
- 分散式系統模式 - Martin Fowler 的分散式模式合集