本章節涵蓋分散式系統的核心理論,包括 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 的變體,為了提高效能而出現:

概念全稱說明
BABasic Availability系統可以暫時不可用,但會快速恢復
SSoft-state服務可以暫時保存狀態,但不保證強一致
EEventual 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: 已提交
    end

2PC 的問題:

  • 同步阻塞:所有參與者都要等待
  • 單點故障:協調者掛掉會導致事務卡住
  • 資料不一致風險:部分節點收到 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 -->|任一失敗| Cancel
TCC 實際案例:電商下單
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。

補償機制的設計要點#

  1. 服務的冪等性:同一請求多次執行結果相同
  2. 狀態機管理:記錄業務流程的狀態變遷
  3. 工作流引擎:用於編排和管理補償流程
  4. 資源預留機制:如電商的庫存預占 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長事務、跨服務編排需要補償邏輯、最終一致