測驗:分散式系統 — Sharding、Replication、CAP Theorem、Message Queues
共 5 題,點選答案後會立即顯示結果
1. Sharding(分片)的核心概念是什麼?
2. 在 CAP Theorem 中,「P」代表 Partition Tolerance(分區容錯)。在分散式系統中,為什麼實際上只需要在 C 和 A 之間選擇?
3. 主從複製(Leader-Follower)架構中,讀寫請求如何分配?
4. 使用者下單後需要扣庫存、寄確認信、通知物流。使用 Message Queue 處理的主要好處是什麼?
5. 一個電商系統使用 country 作為分片鍵,結果美國使用者佔了 60% 的流量。這個問題在文章中被稱為什麼?該如何理解它?
**系列**:第 5 篇(完結篇),共 5 篇|**難度**:L2-進階
**前置知識**:第 3 篇的水平擴展概念、第 4 篇的資料庫基礎(SQL/NoSQL)
**來源影片**:20 System Design Concepts Explained in 10 Minutes (NeetCode, YouTube)
一句話說明
當一台機器裝不下所有資料、撐不住所有流量時,你需要「分散式系統」的四大核心手段:分片、複製、取捨定理、訊息佇列。
為什麼你要看懂這些?
當你用 AI 生成後端服務,AI 可能會在配置裡寫出這樣的東西:
# docker-compose.yml 裡突然出現的分散式設定
services:
redis-master:
image: redis
redis-replica:
image: redis
command: redis-server --replicaof redis-master 6379
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
Code language: CSS (css)# AI 寫出的資料庫路由邏輯
def get_db_shard(user_id: int):
shard_index = user_id % 4 # 根據 user_id 決定用哪台資料庫
return db_connections[shard_index]
Code language: PHP (php)如果你看不懂 replicaof、shard 這些詞,就無法判斷 AI 的架構設計是否合理。這篇會讓你看懂這些概念。
概念一:Sharding(分片)
一句話說明
把一個巨大的資料庫「切塊」,分散存到不同機器上。
圖書館比喻
想像一間圖書館藏書 100 萬本,書架已經擺不下了。解法是什麼?
開分館:
| 分館 | 負責的書 | 分類方式 |
|---|---|---|
| 東區分館 | 作者姓名 A-F | 按姓名首字母 |
| 南區分館 | 作者姓名 G-M | 按姓名首字母 |
| 西區分館 | 作者姓名 N-S | 按姓名首字母 |
| 北區分館 | 作者姓名 T-Z | 按姓名首字母 |
這就是 Sharding。每間分館是一個 shard(分片),而「按作者姓名首字母分」就是 shard key(分片鍵)。
最小範例
當你看到 AI 生成這樣的程式碼時:
# 這就是最基本的 sharding 邏輯
def get_shard(user_id: int) -> str:
shard_index = user_id % 3 # 用取餘數決定分到哪一片
shards = ["db-0", "db-1", "db-2"] # 三台資料庫
return shards[shard_index]
# user_id=1 → db-1, user_id=2 → db-2, user_id=3 → db-0
Code language: PHP (php)翻譯:「有 3 台資料庫,用 user_id 除以 3 的餘數,決定這筆資料存在哪台。」
分片鍵的選擇至關重要
分片鍵選得好不好,直接決定系統的生死:
分片鍵 = user_id(好)
→ 每個使用者的資料集中在同一台,查詢快
→ 使用者均勻分布,每台負載差不多
分片鍵 = country(不好)
→ 美國使用者可能佔 60%,全擠在同一台
→ 這台超載,其他台閒置 → 這就是「Hot Spot(熱點)」問題
Hot Spot 問題
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Shard A │ │ Shard B │ │ Shard C │
│ country=US │ │ country=JP │ │ country=TW │
│ ████████████│ │ ██ │ │ ███ │
│ 60% 流量 │ │ 15% 流量 │ │ 25% 流量 │
│ → 超載! │ │ → 閒置 │ │ → 正常 │
└──────────────┘ └──────────────┘ └──────────────┘
↑ Hot Spot:一個分片承受過多流量
翻譯:就像圖書館把「所有漫畫」放在同一間分館,結果那間分館天天排隊爆滿,其他分館空空的。
必看懂 vs 知道就好
必看懂(會在架構討論中出現):
- shard / sharding → 把資料分散到多台機器
- shard key → 決定資料分到哪台的依據
- hot spot → 某台機器承受過多流量
知道就好(遇到再查):
- consistent hashing → 進階的分片策略,減少重新分配
- resharding → 增減機器時重新分配資料
概念二:Replication(複製)
一句話說明
把同一份資料「複製」到多台機器上,一台掛了其他的能接手。
圖書館比喻
上一節是「開分館,每間放不同的書」。Replication 不一樣,它是「每間分館都放同樣的暢銷書」。
為什麼?因為暢銷書太多人要借了,只有一本根本不夠。複製幾本出來,讀者可以到最近的分館借。
兩種複製模式
模式一:主從複製(Leader-Follower / Master-Slave)
寫入請求
│
▼
┌─────────────┐
│ Leader │ ← 只有這台能寫入
│ (主節點) │
└──────┬──────┘
│ 複製資料
┌─────┼─────┐
▼ ▼ ▼
┌──────┐┌──────┐┌──────┐
│Follower││Follower││Follower│ ← 這些只能讀取
│ 從節點 ││ 從節點 ││ 從節點 │
└──────┘└──────┘└──────┘
▲ ▲ ▲
└─────┼─────┘
│
讀取請求(分散到多台)
翻譯:「一個老闆(Leader)負責收新書,三個員工(Follower)負責借書給讀者。老闆收到新書後,會影印三份分給員工。」
當你看到 AI 寫出這樣的配置:
# Redis 主從複製配置
redis-master:
image: redis
ports: ["6379:6379"]
redis-replica-1:
image: redis
command: redis-server --replicaof redis-master 6379 # 從 master 複製資料
Code language: PHP (php)# 讀寫分離的程式碼
def write_data(key, value):
master_redis.set(key, value) # 寫入走主節點
def read_data(key):
return replica_redis.get(key) # 讀取走從節點,分散負載
Code language: PHP (php)翻譯:「寫入永遠找主節點,讀取分散到從節點,這樣主節點壓力就小了。」
模式二:多主複製(Multi-Master / Leader-Leader)
┌─────────────┐ ┌─────────────┐
│ Leader A │◄───►│ Leader B │
│ 台北機房 │ │ 東京機房 │
│ 可讀可寫 │ │ 可讀可寫 │
└─────────────┘ └─────────────┘
互相同步資料
翻譯:「兩間分館都能收新書,而且會互相通知。台北收到一本新書,東京也會同步一份。」
兩者比較
| 特性 | 主從複製 | 多主複製 |
|---|---|---|
| 寫入點 | 只有一個 | 多個 |
| 複雜度 | 較低 | 較高(要處理衝突) |
| 適用場景 | 讀多寫少(如部落格) | 多地區部署(如跨國服務) |
| 衝突問題 | 不存在 | 兩邊同時改同一筆資料怎麼辦? |
必看懂 vs 知道就好
必看懂:
- replica / replication → 資料複製到多台
- leader / master → 負責寫入的主節點
- follower / replica / slave → 負責讀取的從節點
- 讀寫分離 → 寫走主節點,讀走從節點
知道就好:
- replication lag → 從節點同步有延遲,可能讀到舊資料
- failover → 主節點掛了,自動把從節點升為主節點
概念三:CAP Theorem(CAP 定理)
一句話說明
分散式系統在網路出問題時,只能在「資料一致」和「持續服務」之間二選一。
三個字母是什麼意思
C = Consistency(一致性)
每次讀取都能拿到最新的資料
A = Availability(可用性)
每次請求都能得到回應(不會報錯)
P = Partition Tolerance(分區容錯)
網路斷線時系統還能運作
為什麼只能「三選二」?
先講結論:P(分區容錯)在分散式系統中是必選的,因為網路一定可能斷線。所以實際上是在 C 和 A 之間做選擇。
用一個場景解釋:
情境:台北和東京兩個資料中心,中間網路斷了
台北機房 東京機房
┌─────────────┐ ┌─────────────┐
│ 餘額 = $100 │── X ──│ 餘額 = $100 │
└─────────────┘ 斷線 └─────────────┘
使用者在台北存了 $50,餘額變成 $150
但因為網路斷了,東京那邊還是顯示 $100
這時候怎麼辦?
選 CP(一致性 + 分區容錯):
→ 東京機房暫停服務,等到網路恢復再同步
→ 使用者在東京會收到「服務暫時不可用」
→ 保證不會讀到錯誤的餘額
選 AP(可用性 + 分區容錯):
→ 東京機房繼續服務,但顯示的是舊資料 $100
→ 使用者不會收到錯誤,但看到的資料可能不是最新的
→ 等網路恢復後再慢慢同步
實際系統怎麼選?
| 系統類型 | 選擇 | 原因 |
|---|---|---|
| 銀行轉帳 | CP | 絕對不能顯示錯誤餘額,寧可暫停服務 |
| 社交媒體動態 | AP | 少看到一則貼文沒關係,但不能「打不開 App」 |
| 購物車 | AP | 先讓使用者加購物車,庫存之後再確認 |
| 庫存扣減 | CP | 不能超賣,寧可顯示「系統忙碌」 |
當你看到 AI 推薦不同的資料庫時
AI 說:「我建議用 MongoDB」
→ MongoDB 預設偏向 CP(可設定為 AP)
→ 適合需要一致性的場景
AI 說:「我建議用 Cassandra」
→ Cassandra 偏向 AP
→ 適合高可用、能容忍短暫不一致的場景
AI 說:「我建議用 Redis Cluster」
→ Redis 偏向 AP
→ 適合快取場景,資料短暫不一致可接受
翻譯:「資料庫的選擇反映了系統對 CAP 的取捨。看到 AI 推薦某個資料庫時,問自己:這個系統需要的是『一致性』還是『可用性』?」
必看懂 vs 知道就好
必看懂:
- CAP 三個字母分別代表什麼
- 實際上是在 C 和 A 之間選
- 不同系統有不同的取捨
知道就好:
- PACELC 定理 → CAP 的延伸版本
- eventual consistency(最終一致性)→ AP 系統的常見策略
- quorum → 分散式系統中「多數決」的投票機制
概念四:Message Queues(訊息佇列)
一句話說明
服務之間不直接對話,而是把訊息丟進排隊區,對方有空再來處理。
餐廳比喻
傳統做法(同步、直接呼叫):
客人 → 直接對廚師喊 → 廚師做完才能接下一單
問題:廚師忙不過來時,客人只能乾等
Message Queue 做法(非同步、解耦):
客人 → 點單寫在紙上 → 放到出單台 → 廚師依序取單處理
好處:客人點完就可以去聊天了,廚師按自己速度做
┌────────┐ ┌──────────────┐ ┌────────────┐
│ 客人 │───►│ 出單台 │───►│ 廚師 │
│(Producer)│ │(Message Queue)│ │ (Consumer) │
│ 生產者 │ │ 訊息佇列 │ │ 消費者 │
└────────┘ └──────────────┘ └────────────┘
客人不需要知道哪個廚師在做
廚師不需要知道哪個客人點的
→ 這就是「解耦」
最小範例
當你看到 AI 寫出這樣的程式碼:
# 生產者(Producer):把任務丟進 Queue
import pika # RabbitMQ 的 Python 套件
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='order_queue') # 建立一個叫 order_queue 的佇列
channel.basic_publish(
exchange='',
routing_key='order_queue',
body='{"order_id": 123, "item": "拿鐵"}' # 把訂單丟進佇列
)
Code language: PHP (php)翻譯:「連上 RabbitMQ,建立一個叫 order_queue 的排隊區,把一筆訂單丟進去。」
# 消費者(Consumer):從 Queue 取出任務處理
def process_order(ch, method, properties, body):
order = json.loads(body) # 取出訂單
print(f"正在製作:{order['item']}") # 處理訂單
ch.basic_ack(delivery_tag=method.delivery_tag) # 告訴 Queue:我處理完了
channel.basic_consume(
queue='order_queue',
on_message_callback=process_order # 有新訂單時呼叫這個函式
)
channel.start_consuming() # 開始持續監聽
Code language: PHP (php)翻譯:「持續監聽 order_queue,有新訂單就取出來處理,處理完回報。」
為什麼要用 Message Queue?
場景:使用者下單後要做三件事
1. 扣庫存
2. 寄確認信
3. 通知物流
沒有 Message Queue(同步):
使用者按下單 → 等扣庫存 → 等寄信 → 等通知物流 → 終於收到「下單成功」
總耗時:300ms + 500ms + 200ms = 1000ms
問題:寄信服務掛了 → 整個下單失敗!
有 Message Queue(非同步):
使用者按下單 → 扣庫存 → 把「寄信」和「通知物流」丟進 Queue → 馬上回「下單成功」
使用者等待:300ms
寄信和物流在背景慢慢處理
寄信服務掛了?沒關係,Queue 會保留訊息,等它恢復再處理
常見的 Message Queue 工具
| 工具 | 定位 | 一句話翻譯 |
|---|---|---|
| RabbitMQ | 傳統訊息佇列 | 「郵局」:確保每封信都送達 |
| Apache Kafka | 分散式串流平台 | 「廣播電台」:訊息可以被多個消費者重複讀取 |
| Redis Pub/Sub | 輕量發布/訂閱 | 「對講機」:即時傳話,但不保留歷史 |
| Amazon SQS | 雲端託管佇列 | 「雲端郵局」:不用自己架設 |
當你看到 AI 在 docker-compose 裡加了 RabbitMQ 或 Kafka
# AI 生成的 docker-compose.yml
services:
app:
build: .
depends_on:
- rabbitmq # 應用依賴 RabbitMQ
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672" # AMQP 協議埠(程式連線用)
- "15672:15672" # 管理介面(瀏覽器開 localhost:15672)
worker:
build: .
command: python worker.py # 消費者,持續處理 Queue 裡的任務
depends_on:
- rabbitmq
Code language: PHP (php)翻譯:「這個架構有三個角色:app 負責丟任務進 Queue,rabbitmq 是那個 Queue,worker 負責從 Queue 取任務處理。」
必看懂 vs 知道就好
必看懂:
- producer / publisher → 丟訊息進 Queue 的人
- consumer / subscriber → 從 Queue 取訊息處理的人
- queue → 訊息排隊的地方
- async / 非同步 → 丟了就走,不用等結果
知道就好:
- dead letter queue → 處理失敗的訊息去的地方
- acknowledgment (ack) → 消費者回報「我處理完了」
- backpressure → Queue 滿了,要求生產者慢一點
Code language: JavaScript (javascript)Vibe Coder 檢查點
當你看到 AI 生成的架構包含分散式概念時,確認以下幾點:
- [ ] Sharding:AI 選了什麼 shard key?會不會造成 hot spot?
- [ ] Replication:是主從還是多主?讀寫分離的邏輯正確嗎?
- [ ] CAP 取捨:這個系統需要一致性還是可用性?資料庫選對了嗎?
- [ ] Message Queue:哪些操作真的需要非同步?Queue 掛了會怎樣?
- [ ] 整體思考:這個系統真的需要分散式架構嗎?單機能不能解決?
系列總結:20 個概念的全景圖
走過五篇文章,我們已經認識了系統設計的 20 個核心概念。現在把它們串起來,看看一個完整的系統長什麼樣。
一個請求的旅程
想像一個使用者打開手機 App,瀏覽一個電商網站:
使用者打開 App
│
▼
┌─────────────────────────────────────────────────────┐
│ 第 1 篇:網路基礎 │
│ │
│ 1. IP Address → 找到伺服器在哪裡 │
│ 2. DNS → 把網址翻譯成 IP │
│ 3. HTTP/HTTPS → 送出請求、收到回應 │
│ 4. TCP/UDP → 確保資料完整送達 │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 第 2 篇:API 與代理 │
│ │
│ 5. API → 前端和後端溝通的介面 │
│ 6. REST API → 用 HTTP 動詞操作資源 │
│ 7. GraphQL → 前端精準要求需要的資料 │
│ 8. gRPC → 微服務之間高效率通訊 │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 第 3 篇:擴展與效能 │
│ │
│ 9. Proxy / Reverse Proxy → 請求先經過代理 │
│ 10. Load Balancer → 分配流量到多台伺服器 │
│ 11. Caching → 快取常用資料,加速回應 │
│ 12. CDN → 靜態資源放到離使用者最近的節點 │
│ 13. 水平擴展 vs 垂直擴展 → 加機器 vs 升級機器 │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 第 4 篇:資料庫與儲存 │
│ │
│ 14. SQL → 結構化資料、關聯查詢 │
│ 15. NoSQL → 彈性結構、水平擴展 │
│ 16. ACID → 交易的四大保證 │
│ 17. Blob Storage → 圖片、影片等大檔案儲存 │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 第 5 篇:分散式系統(本篇) │
│ │
│ 18. Sharding → 資料太多,分散到多台機器 │
│ 19. Replication → 資料備份,提高可用性 │
│ 20. CAP Theorem → 一致性 vs 可用性的取捨 │
│ + Message Queues → 服務之間非同步溝通 │
└─────────────────────────────────────────────────────┘
Code language: JavaScript (javascript)完整架構:全部概念合在一起
使用者
│
┌──────┴──────┐
│ DNS (2) │ 把網址翻譯成 IP
└──────┬──────┘
│
┌──────┴──────┐
│ CDN (12) │ 靜態資源就近取得
└──────┬──────┘
│
┌──────┴──────┐
│ Load │
│ Balancer(10) │ 分配流量
└──────┬──────┘
┌─────┼─────┐
▼ ▼ ▼
┌─────┐┌─────┐┌─────┐
│App 1││App 2││App 3│ 水平擴展 (13)
└──┬──┘└──┬──┘└──┬──┘
│ │ │
┌──────┴──────┴──────┴──────┐
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Cache (11) │ │ Message │
│ Redis │ │ Queue (MQ) │
└──────┬──────┘ └──────┬──────┘
│ │
│ ┌──────┴──────┐
│ │ Workers │
│ │ 背景處理任務 │
│ └─────────────┘
│
┌──────┴──────┐
│ Database │
│ Cluster │
├─────────────┤
│ Sharding(18)│ 資料分片
│ Replica (19)│ 讀寫分離
│ CAP取捨 (20)│ 一致性 vs 可用性
└──────┬──────┘
│
┌──────┴──────┐
│ Blob │
│ Storage (17) │ 圖片影片儲存
└─────────────┘
五篇的學習路徑回顧
| 篇數 | 主題 | 回答的核心問題 |
|---|---|---|
| #01 | 網路基礎 | 請求怎麼從瀏覽器送到伺服器? |
| #02 | API 與代理 | 前端和後端怎麼溝通? |
| #03 | 擴展與效能 | 流量大了怎麼辦? |
| #04 | 資料庫與儲存 | 資料怎麼存、怎麼查? |
| #05 | 分散式系統 | 一台機器不夠時怎麼協作? |
系列完結語
系統設計不是「背答案」,而是理解「為什麼要這樣做」。
作為 Vibe Coder,你不需要從零手寫一個分散式系統。但當 AI 幫你生成了一個包含 Redis Cluster、RabbitMQ、多台資料庫的架構時,你需要看得懂:
- 這些元件分別在做什麼(本系列教你的)
- 這樣的設計合不合理(你的判斷力)
- 哪裡可能出問題(檢查點幫你確認)
20 個概念,5 篇文章,就是你看懂系統架構的基本工具箱。遇到新概念時,回來翻翻這個全景圖,找到它在架構中的位置,就能快速理解它的角色。
進階測驗:分散式系統 — Sharding、Replication、CAP Theorem、Message Queues
共 5 題,包含情境題與錯誤診斷題。