# 系統設計方法論

::: tip 前言
**系統設計不是拍腦袋畫架構圖，而是一套有章可循的方法論。** 無論是面試中的系統設計題，還是實際工作中的架構設計，都遵循相似的思考框架：先搞清楚問題，再估算規模，然後設計方案，最後深入最佳化。
:::

**這篇文章會帶你學什麼？**

學完這章後，你將獲得：

- **設計流程**：掌握系統設計的四步法框架
- **容量估算**：學會「信封背面估算」的技巧
- **常見模式**：熟悉快取、分庫分表、訊息佇列等核心模式
- **權衡思維**：理解架構設計中的 trade-off 思維
- **實戰案例**：透過短鏈服務、Feed 流等案例理解設計過程

| 章節 | 內容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 設計四步法 | 需求澄清、容量估算、架構設計、深入最佳化 |
| **第 2 章** | 容量估算 | QPS、儲存、頻寬、信封背面估算 |
| **第 3 章** | 核心設計模式 | 快取、分庫分表、訊息佇列、CDN |
| **第 4 章** | 權衡思維 | 一致性 vs 可用性、效能 vs 成本 |
| **第 5 章** | 經典案例 | 短鏈服務、Feed 流、秒殺系統 |

---

## 1. 系統設計四步法

系統設計不是一上來就畫架構圖。無論是面試還是實戰，都應該遵循一個結構化的流程。

<SystemDesignStepsDemo />

::: tip 為什麼要先澄清需求？
很多人拿到題目就開始畫圖，結果設計了一個「正確但不是面試官想要的」系統。花 5 分鐘問清楚需求，能避免後面 30 分鐘的返工。

常見的澄清問題：
- 系統的核心功能是什麼？（不要設計所有功能）
- 使用者規模多大？（決定是否需要分散式）
- 讀寫比例？（決定快取策略）
- 資料需要保留多久？（決定儲存方案）
:::

---

## 2. 容量估算：信封背面的藝術

「信封背面估算」（Back-of-envelope estimation）是系統設計中的核心技能。不需要精確計算，只需要知道量級。

<CapacityEstimationDemo />

### 常用換算速查

| 量級 | 換算 | 記憶技巧 |
|------|------|---------|
| 1 天 | 86,400 秒 | ≈ 10 萬秒 |
| 1 億請求/天 | ≈ 1,200 QPS | 除以 10 萬 |
| 1 KB × 1 億 | ≈ 100 GB | 1 億條小記錄 |
| 1 MB × 100 萬 | ≈ 1 TB | 100 萬張圖片 |

### 2-8 法則在估算中的應用

大多數系統遵循 80/20 法則：20% 的資料承載 80% 的請求。這意味著：

- **快取大小** ≈ 總資料量 × 20%
- **熱點 QPS** ≈ 總 QPS × 80% 集中在 20% 的 key 上
- **快取命中率**目標 ≈ 80%+（低於這個值說明快取策略有問題）

---

## 3. 核心設計模式

系統設計中反覆出現的模式，掌握這些就能應對大多數場景。

### 3.1 快取模式

| 模式 | 讀路徑 | 寫路徑 | 適用場景 |
|------|--------|--------|---------|
| Cache-Aside | 先查快取，miss 則查 DB 並回填 | 先寫 DB，再刪快取 | 通用場景，最常用 |
| Read-Through | 快取層自動從 DB 載入 | 同 Cache-Aside | 需要快取框架支援 |
| Write-Behind | 同 Cache-Aside | 先寫快取，非同步寫 DB | 寫密集型，可容忍丟資料 |

::: tip 為什麼是「刪快取」而不是「更新快取」？
更新快取在並發場景下容易出現資料不一致：執行緒 A 和 B 同時更新，A 先寫 DB 但 B 先更新快取，導致快取中是 B 的舊值。刪除快取則讓下次讀請求重新從 DB 載入，天然避免這個問題。
:::

### 3.2 分庫分表

當單表資料量超過千萬級，或單庫 QPS 超過瓶頸時，就需要考慮分庫分表。

| 策略 | 做法 | 優點 | 缺點 |
|------|------|------|------|
| 垂直分庫 | 按業務域拆分資料庫 | 業務解耦，獨立擴展 | 跨庫 JOIN 困難 |
| 水平分表 | 同一張表按規則拆成多張 | 單表資料量可控 | 分片鍵選擇關鍵 |
| 垂直分表 | 把大欄位拆到獨立表 | 減少 IO，提升查詢效率 | 需要額外 JOIN |

**分片鍵選擇原則**：
- 選擇查詢最頻繁的欄位（如 user_id）
- 資料分佈要均勻，避免熱點
- 盡量讓同一使用者的資料在同一分片（減少跨分片查詢）

### 3.3 訊息佇列

訊息佇列是分散式系統的「減震器」，核心作用是解耦、非同步、削峰。

| 場景 | 不用佇列 | 用佇列 |
|------|---------|--------|
| 下單後發通知 | 下單介面同步呼叫通知服務，通知失敗導致下單失敗 | 下單成功後發訊息，通知服務非同步消費 |
| 秒殺搶購 | 瞬間流量打爆資料庫 | 請求先入佇列，後端按能力消費 |
| 資料同步 | 服務 A 直接呼叫服務 B 的介面 | 服務 A 發事件，服務 B 訂閱處理 |

---

## 4. 權衡思維：沒有銀彈

架構設計的本質是權衡（Trade-off）。每個決策都有代價，關鍵是理解代價並做出適合當前階段的選擇。

| 權衡維度 | 選項 A | 選項 B | 決策依據 |
|---------|--------|--------|---------|
| 一致性 vs 可用性 | 強一致（CP） | 高可用（AP） | 業務能否容忍短暫不一致？ |
| 效能 vs 成本 | 全量快取 | 按需快取 | 資料量和預算 |
| 簡單 vs 靈活 | 單體架構 | 微服務 | 團隊規模和業務複雜度 |
| 即時 vs 批量 | 串流處理 | 批處理 | 資料時效性要求 |
| 自建 vs 託管 | 自己搭 MySQL | 用雲端資料庫 RDS | 維運能力和成本 |

::: tip 架構決策記錄（ADR）
每個重要的架構決策都應該記錄下來：**背景是什麼、考慮了哪些方案、為什麼選了這個、有什麼代價**。這不是為了推卸責任，而是為了讓後來的人理解「為什麼當時這麼設計」。

格式很簡單：
- **標題**：用 XXX 替代 YYY
- **背景**：我們遇到了什麼問題
- **決策**：我們選擇了什麼方案
- **理由**：為什麼選這個
- **代價**：這個決策的缺點和風險
:::

### 常見的錯誤權衡

| 錯誤 | 表現 | 正確做法 |
|------|------|---------|
| 過早最佳化 | 日活 1000 就上分庫分表 | 先用單庫，遇到瓶頸再拆 |
| 技術驅動 | 「我想用 Kafka」而不是「我需要非同步」 | 從問題出發，而非從技術出發 |
| 忽略維運成本 | 選了最佳方案但團隊維護不了 | 方案要匹配團隊能力 |
| 追求完美一致性 | 所有場景都用分散式事務 | 大多數場景最終一致性就夠了 |

---

## 5. 經典案例

透過三個經典案例，把前面學到的方法論串起來。

### 5.1 短鏈服務（TinyURL）

短鏈服務是系統設計面試的經典題目，麻雀雖小五臟俱全。

**需求澄清**：
- 核心功能：長連結 → 短連結（寫），短連結 → 重定向（讀）
- 讀寫比：約 100:1（讀遠多於寫）
- 日均重定向：1 億次
- 短鏈永不過期

**容量估算**：

| 指標 | 計算 | 結果 |
|------|------|------|
| 寫 QPS | 1 億 / 100 / 86400 | ≈ 12 QPS |
| 讀 QPS | 1 億 / 86400 | ≈ 1,200 QPS |
| 峰值讀 QPS | 1,200 × 3 | ≈ 3,600 QPS |
| 5 年儲存 | 100 萬/天 × 365 × 5 × 100B | ≈ 18 GB |
| 快取（20%） | 18 GB × 20% | ≈ 3.6 GB |

**架構設計**：

```
寫路徑：客戶端 → API Server → ID 產生器 → Base62 編碼 → 寫入 MySQL + Redis
讀路徑：客戶端 → CDN → API Server → Redis 查詢 → 302 重定向
                                    ↓ (cache miss)
                                  MySQL 查詢 → 回填 Redis
```

**關鍵設計決策**：
- 短碼產生：Snowflake 分散式 ID + Base62 編碼，避免雜湊衝突
- 快取策略：Cache-Aside，熱點短鏈用 CDN 加速
- 資料庫：單表即可（18GB 很小），按短碼做索引

### 5.2 Feed 流系統

社交平台的 Feed 流（朋友圈、微博首頁）是另一個經典題目。

**核心挑戰**：使用者發一則動態，如何讓所有關注者看到？

| 方案 | 做法 | 優點 | 缺點 |
|------|------|------|------|
| 拉模式（Pull） | 讀取時即時聚合關注者的動態 | 寫入簡單，儲存少 | 讀取慢，關注多時延遲高 |
| 推模式（Push） | 發布時寫入所有粉絲的收件匣 | 讀取極快 | 大 V 發動態寫擴散嚴重 |
| 推拉結合 | 普通使用者推，大 V 拉 | 平衡讀寫效能 | 實作複雜 |

**推拉結合方案**：
- 粉絲數 < 1 萬：發布時推送到所有粉絲的 Feed 快取（推模式）
- 粉絲數 > 1 萬：不推送，粉絲讀取時即時拉取（拉模式）
- 使用者打開 Feed 時：合併推送的內容 + 即時拉取大 V 的內容，按時間排序

### 5.3 秒殺系統

秒殺的核心挑戰：瞬間超高並發 + 庫存不能超賣。

**流量特徵**：
- 活動開始前：大量使用者重新整理頁面等待
- 活動開始瞬間：QPS 可能是平時的 100 倍以上
- 活動結束後：流量迅速回落

**分層削峰策略**：

```
使用者請求 → CDN（靜態頁面）→ 閘道（限流）→ 訊息佇列（削峰）→ 庫存服務（扣減）
```

| 層級 | 策略 | 效果 |
|------|------|------|
| 前端 | 按鈕置灰 + 隨機延遲 + 驗證碼 | 過濾機器人，分散請求 |
| CDN | 靜態資源快取 | 減少 90% 的頁面請求 |
| 閘道 | 令牌桶限流 | 只放行系統能承受的流量 |
| 訊息佇列 | 請求入佇列，非同步處理 | 削峰填谷，保護資料庫 |
| 庫存服務 | Redis 預扣減 + Lua 原子操作 | 防止超賣，毫秒級回應 |

::: tip 秒殺的核心原則
1. **盡量攔截在上游**：能在 CDN 擋住的就不要到應用層
2. **讀寫分離**：商品詳情頁走快取，只有下單走資料庫
3. **非同步處理**：使用者點擊「搶購」後立即回傳「排隊中」，背景非同步處理
4. **兜底方案**：限流、熔斷、降級，任何一層出問題都有 Plan B
:::

---

## 總結

系統設計是一門實踐性很強的技能，核心在於結構化思考和權衡取捨。

回顧本章的關鍵要點：

1. **四步法框架**：需求澄清 → 容量估算 → 架構設計 → 深入最佳化，每一步都不可跳過
2. **信封背面估算**：不需要精確，只需要知道量級，用於指導架構決策
3. **核心模式**：快取、分庫分表、訊息佇列、CDN、限流熔斷——這些是系統設計的「積木」
4. **權衡思維**：沒有完美方案，只有適合當前階段的方案，記錄每個決策的理由和代價
5. **經典案例**：短鏈服務練基礎、Feed 流練推拉模型、秒殺練高並發——掌握這三個就能舉一反三

## 延伸閱讀

- [System Design Interview](https://www.amazon.com/System-Design-Interview-insiders-Second/dp/B08CMF2CQF) - Alex Xu 系統設計面試經典
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 資料密集型應用設計
- [The System Design Primer](https://github.com/donnemartin/system-design-primer) - GitHub 上最全的系統設計學習資源
- [ByteByteGo](https://bytebytego.com/) - Alex Xu 的系統設計視覺化部落格
