本章節涵蓋分散式系統的核心理論,包括 CAP 定理、BASE 理論、一致性模型與分散式事務。
CAP 定理#
CAP 定理是分散式系統設計的基石,由 Eric Brewer 提出:
在網路發生分區 (Partition) 時,你只能在資料的一致性 (Consistency) 與可用性 (Availability) 之間二擇一。
CAP 三要素#
| 要素 | 說明 |
|---|---|
| Consistency (一致性) | 所有節點在同一時間看到相同的資料 |
| Availability (可用性) | 每個請求都能得到回應(不保證資料是最新的) |
| Partition Tolerance (分區容忍) | 系統在網路分區時仍能繼續運作 |
CAP 的權衡#
分散式系統 = 必須容忍分區 (P 是必選)
→ 在 C 和 A 之間選擇- CP 系統:選擇一致性,犧牲可用性(如 ZooKeeper)
- AP 系統:選擇可用性,犧牲一致性(如 Cassandra)
CAP 定理與擴展性 (Scalability) 無關,無論是分片 (Sharded) 或非分片的系統都適用。
BASE 理論#
BASE 是 ACID 的變體,為了提高效能而出現:
| 概念 | 全稱 | 說明 |
|---|---|---|
| BA | Basic Availability | 系統可以暫時不可用,但會快速恢復 |
| S | Soft-state | 服務可以暫時保存狀態,但不保證強一致 |
| E | Eventual Consistency | 最終一致性,短暫的不一致是可接受的 |
ACID vs BASE#
ACID (酸) = 強調一致性 (CAP 中的 C)
BASE (鹼) = 強調可用性 (CAP 中的 A)電商下單場景:ACID vs BASE
ACID 玩法(強一致性):
- 每個購買請求都需要把庫存鎖住
- 減完庫存後釋放鎖,後續用戶才能購買
- 結果:同一時間只能有一個用戶下單,吞吐量低
BASE 玩法(最終一致性):
- 所有用戶可以同時下單
- 系統非同步批量處理訂單
- 如果庫存不足,事後通知用戶訂單取消
- 結果:高吞吐量,但可能有超賣風險
這也是為什麼 Amazon 的訂單系統會先發送「收到訂單」的郵件,過一會兒才發送「訂單確認」的郵件。
一致性模型#
強一致性 (Strong Consistency)#
任何時刻,所有節點看到的資料都是一致的。
實現方式:
- 兩階段提交 (2PC)
- Paxos/Raft 共識演算法
代價:
- 效能較低
- 可用性受影響
最終一致性 (Eventual Consistency)#
系統保證在沒有新的更新操作後,最終所有節點的資料會變成一致。
變體:
- 因果一致性 (Causal Consistency):有因果關係的操作順序被保證
- 讀己之寫 (Read Your Writes):用戶總是能讀到自己寫入的資料
- 單調讀 (Monotonic Reads):一旦讀到某個版本,不會讀到更舊的版本
分散式事務#
兩階段提交 (2PC)#
sequenceDiagram
participant C as 協調者
participant P1 as 參與者1
participant P2 as 參與者2
rect rgb(200, 220, 255)
Note over C,P2: 第一階段 (Prepare)
C->>P1: 準備好了嗎?
C->>P2: 準備好了嗎?
P1-->>C: 準備好了
P2-->>C: 準備好了
end
rect rgb(200, 255, 200)
Note over C,P2: 第二階段 (Commit)
C->>P1: 提交
C->>P2: 提交
P1-->>C: 已提交
P2-->>C: 已提交
end2PC 的問題:
- 同步阻塞:所有參與者都要等待
- 單點故障:協調者掛掉會導致事務卡住
- 資料不一致風險:部分節點收到 commit 後掛掉
TCC (Try-Confirm-Cancel)#
flowchart LR
subgraph Try["Try 階段"]
T1[預留資源]
T2[凍結庫存]
T3[凍結餘額]
end
subgraph Confirm["Confirm 階段"]
C1[確認操作]
C2[扣減庫存]
C3[扣款]
end
subgraph Cancel["Cancel 階段"]
X1[取消操作]
X2[解凍庫存]
X3[解凍餘額]
end
Try -->|全部成功| Confirm
Try -->|任一失敗| CancelTCC 實際案例:電商下單
Try 階段:
- 訂單服務:創建訂單(狀態:待確認)
- 庫存服務:凍結庫存
- 支付服務:凍結餘額
Confirm 階段(全部成功):
- 訂單服務:確認訂單
- 庫存服務:扣減庫存
- 支付服務:扣款
Cancel 階段(任一失敗):
- 訂單服務:取消訂單
- 庫存服務:解凍庫存
- 支付服務:解凍餘額Saga 模式#
將長事務拆分成多個本地事務,每個本地事務都有對應的補償操作:
flowchart LR
T1[T1] --> T2[T2] --> T3[T3] --> T4[T4]
T4 -.->|失敗| C3[C3 補償]
C3 -.-> C2[C2 補償]
C2 -.-> C1[C1 補償]
style T4 fill:#ffcccc
style C1 fill:#ffffcc
style C2 fill:#ffffcc
style C3 fill:#ffffcc兩種協調方式:
- Choreography(編排式):各服務通過事件互相協調
- Orchestration(編制式):由中央協調者統一控制流程
分散式 ID 生成#
需求#
在分散式系統中,需要一個全局唯一的 ID 來標識交易或實體。
UUID#
- 優點:衝突機率極低,無需中心化服務
- 缺點:字串長、索引效率低、無遞增性、不易閱讀
Snowflake 演演算法#
Twitter 的開源分散式 ID 生成方案,產生 64 位元的 long 型 ID:
| 1 bit | 41 bits | 10 bits | 12 bits |
| 符號 | 毫秒時間戳 | 機器編號 | 序列號 |
| 0 | 約 69.7 年 | 1024 個實體 | 每毫秒 4096 個 |特點:
- 趨勢遞增,適合資料庫索引
- 包含時間資訊
- 去中心化生成
資料庫自增#
使用資料庫的自增序列,透過不同步長支援多節點:
-- 節點 1:步長 3,起始值 1
-- 節點 2:步長 3,起始值 2
-- 節點 3:步長 3,起始值 3
節點 1: 1, 4, 7, 10, ...
節點 2: 2, 5, 8, 11, ...
節點 3: 3, 6, 9, 12, ...補償事務#
當分散式事務無法達成強一致性時,需要業務補償機制:
業務補償的核心思想:先做一個 Plan 的 API 呼叫,然後各個子系統 Reserve 住相應的資源。如果成功則 Commit;如果有任何失敗則整體 Cancel。
補償機制的設計要點#
- 服務的冪等性:同一請求多次執行結果相同
- 狀態機管理:記錄業務流程的狀態變遷
- 工作流引擎:用於編排和管理補償流程
- 資源預留機制:如電商的庫存預占 15 分鐘
flowchart LR
subgraph Forward["正向流程"]
direction LR
F1[請假] --> F2[機票] --> F3[酒店] --> F4[租車]
end
subgraph Compensate["補償流程"]
direction RL
C4[取消租車] --> C3[取消酒店] --> C2[退機票] --> C1[銷假]
end
F4 -.->|失敗時| C4
style Forward fill:#c8e6c9
style Compensate fill:#ffcdd2小結#
| 概念 | 適用場景 | 權衡 |
|---|---|---|
| 強一致性 | 金融交易、庫存扣減 | 效能低、可用性差 |
| 最終一致性 | 訂單處理、通知系統 | 短暫不一致、需補償機制 |
| 2PC | 資料庫層面的分散式事務 | 阻塞、單點故障風險 |
| TCC | 業務層面的分散式事務 | 需要業務改造、邏輯複雜 |
| Saga | 長事務、跨服務編排 | 需要補償邏輯、最終一致 |