系統設計方法論
前言
系統設計不是拍腦袋畫架構圖,而是一套有章可循的方法論。 無論是面試中的系統設計題,還是實際工作中的架構設計,都遵循相似的思考框架:先搞清楚問題,再估算規模,然後設計方案,最後深入最佳化。
這篇文章會帶你學什麼?
學完這章後,你將獲得:
- 設計流程:掌握系統設計的四步法框架
- 容量估算:學會「信封背面估算」的技巧
- 常見模式:熟悉快取、分庫分表、訊息佇列等核心模式
- 權衡思維:理解架構設計中的 trade-off 思維
- 實戰案例:透過短鏈服務、Feed 流等案例理解設計過程
| 章節 | 內容 | 核心概念 |
|---|---|---|
| 第 1 章 | 設計四步法 | 需求澄清、容量估算、架構設計、深入最佳化 |
| 第 2 章 | 容量估算 | QPS、儲存、頻寬、信封背面估算 |
| 第 3 章 | 核心設計模式 | 快取、分庫分表、訊息佇列、CDN |
| 第 4 章 | 權衡思維 | 一致性 vs 可用性、效能 vs 成本 |
| 第 5 章 | 經典案例 | 短鏈服務、Feed 流、秒殺系統 |
1. 系統設計四步法
系統設計不是一上來就畫架構圖。無論是面試還是實戰,都應該遵循一個結構化的流程。
為什麼要先澄清需求?
很多人拿到題目就開始畫圖,結果設計了一個「正確但不是面試官想要的」系統。花 5 分鐘問清楚需求,能避免後面 30 分鐘的返工。
常見的澄清問題:
- 系統的核心功能是什麼?(不要設計所有功能)
- 使用者規模多大?(決定是否需要分散式)
- 讀寫比例?(決定快取策略)
- 資料需要保留多久?(決定儲存方案)
2. 容量估算:信封背面的藝術
「信封背面估算」(Back-of-envelope estimation)是系統設計中的核心技能。不需要精確計算,只需要知道量級。
常用換算速查
| 量級 | 換算 | 記憶技巧 |
|---|---|---|
| 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 | 寫密集型,可容忍丟資料 |
為什麼是「刪快取」而不是「更新快取」?
更新快取在並發場景下容易出現資料不一致:執行緒 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 | 維運能力和成本 |
架構決策記錄(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 原子操作 | 防止超賣,毫秒級回應 |
秒殺的核心原則
- 盡量攔截在上游:能在 CDN 擋住的就不要到應用層
- 讀寫分離:商品詳情頁走快取,只有下單走資料庫
- 非同步處理:使用者點擊「搶購」後立即回傳「排隊中」,背景非同步處理
- 兜底方案:限流、熔斷、降級,任何一層出問題都有 Plan B
總結
系統設計是一門實踐性很強的技能,核心在於結構化思考和權衡取捨。
回顧本章的關鍵要點:
- 四步法框架:需求澄清 → 容量估算 → 架構設計 → 深入最佳化,每一步都不可跳過
- 信封背面估算:不需要精確,只需要知道量級,用於指導架構決策
- 核心模式:快取、分庫分表、訊息佇列、CDN、限流熔斷——這些是系統設計的「積木」
- 權衡思維:沒有完美方案,只有適合當前階段的方案,記錄每個決策的理由和代價
- 經典案例:短鏈服務練基礎、Feed 流練推拉模型、秒殺練高並發——掌握這三個就能舉一反三
延伸閱讀
- System Design Interview - Alex Xu 系統設計面試經典
- Designing Data-Intensive Applications - Martin Kleppmann 資料密集型應用設計
- The System Design Primer - GitHub 上最全的系統設計學習資源
- ByteByteGo - Alex Xu 的系統設計視覺化部落格