系統設計題看起來千變萬化,但拆到底,90% 的題目都是同一組積木拼出來的。把這四塊基礎元件吃透,你就有了應付絕大多數題目的底層詞彙:
- 負載平衡(Load Balancing):把流量分散到多台機器——水平擴展的起點。
- 快取(Caching):把熱資料放近一點——降延遲、擋流量。
- 分片(Sharding):把資料切開放到多台——突破單機容量上限。
- 訊息佇列(Message Queue):把同步變非同步——解耦、削峰、緩衝。
這篇逐塊講「它解什麼問題、怎麼運作、有什麼取捨」,每塊附面試官最常追問的問題。把它當系統設計的「字典」,之後任何題目都能拼出來。
🧱 心法:系統設計不是發明新東西,而是根據需求把這些標準積木組起來,並講清楚每個選擇的取捨。面試官要聽的就是「為什麼選這塊、它的代價是什麼」。
① 負載平衡(Load Balancing)
解什麼問題:單台機器有上限(CPU、連線數)。當流量大過一台能扛的,就需要多台 + 一個「分派員」把請求分散——這就是負載平衡器(LB)。它也順便提供高可用(一台掛了,流量自動導去健康的)。
L4 vs L7:
- L4(傳輸層):只看 IP / port 轉發,快、不懂應用內容。
- L7(應用層):看得懂 HTTP(路徑、header、cookie),能做基於內容的路由(如
/api導 A 群、/img導 B 群)、SSL 終結,但較重。
常見演算法:
- Round Robin:輪流發,最簡單。
- Least Connections:發給當下連線最少的,適合請求耗時不均。
- Consistent Hashing / IP Hash:同一來源固定導到同一台(session 黏著或快取親和性需要)。
🎤 面試官追問:「LB 本身會不會變成單點故障?」(會——所以 LB 要做多副本 + 故障轉移,或用 DNS/Anycast 做多 LB。)「要 session 黏著怎麼辦?」(用 hash 黏著,或更好的做法是把 session 外移到共享儲存(如 Redis)讓伺服器無狀態——無狀態才好水平擴展。)
② 快取(Caching)
解什麼問題:資料庫慢且貴。把高頻讀取的熱資料放到記憶體(如 Redis)或更近的地方,把大量讀取流量擋在 DB 之前——降延遲、提吞吐、護住 DB。
放在哪(多層):瀏覽器 → CDN(靜態資源/邊緣)→ 應用層快取 → 分散式快取(Redis)→ DB 自身快取。
寫入策略:
- Cache-aside(最常見):讀時先查快取,miss 就讀 DB 並回填;寫時更新 DB 並失效(delete)快取。
- Write-through:寫時同時寫快取與 DB(一致性好,寫較慢)。
- Write-back:先寫快取、之後批次刷 DB(快,但有遺失風險)。
逐出(eviction):記憶體有限,用 LRU / LFU / TTL 淘汰。
兩個經典難題:
- 快取失效一致性:DB 變了快取沒變 → 讀到舊資料。常用「更新 DB 後刪快取」+ 短 TTL 兜底。
- 快取雪崩 / 穿透 / 擊穿:大量 key 同時失效(雪崩)、查不存在的資料一直打 DB(穿透)、熱 key 失效瞬間 DB 被打爆(擊穿)。解法:TTL 加隨機抖動、空值也快取、熱 key 加鎖重建。
🎤 面試官追問:「DB 和快取怎麼保持一致?」(談 cache-aside 的「先更 DB 再刪快取」,以及為什麼很難做到強一致——通常接受最終一致 + 短 TTL。)「什麼資料不該快取?」(極少讀、強一致要求、頻繁變動的資料。)
③ 分片 / 分區(Sharding / Partitioning)
解什麼問題:當資料量或寫入量大到單台 DB 裝不下/扛不住,就要把資料水平切成多片(shard),分散到多台。(對照:複本/replica 解的是讀取擴展與高可用,分片解的是寫入與容量擴展。)
分片策略:
- Hash 分片:
shard = hash(key) % N。分布均勻,但範圍查詢困難,且加減機器要大搬資料。 - Range 分片:按 key 範圍切(如 A–M、N–Z)。範圍查詢友善,但容易熱點(某段特別熱)。
- Consistent Hashing(一致性雜湊):加減節點時只搬動少量資料,是分散式快取/DB 常用解,緩解「改 N 就大搬家」。
核心難題:
- 熱點(hotspot):某個 shard 特別忙(如某網紅的資料)。解法:更細的分片鍵、加鹽(salting)、熱資料額外複製。
- 再平衡(rebalancing):加機器時如何最小化資料搬遷——consistent hashing 或虛擬節點。
- 跨片操作變難:跨 shard 的 join、交易、聚合都變複雜——所以分片鍵要選得好(讓常見查詢落在單片)。
🎤 面試官追問:「分片鍵怎麼選?」(選讓多數查詢落在單一 shard、且分布均勻、不易熱點的鍵——例如用 user_id 而非時間。)「為什麼不用
hash % N?」(加減機器要 rehash 幾乎全部資料,所以用 consistent hashing。)「跨片交易怎麼處理?」(盡量避免;必要時用 2PC 或 saga,但代價高。)
④ 訊息佇列(Message Queue)
解什麼問題:同步呼叫把服務綁死——下游慢、整條鏈就卡;下游掛、請求就丟。把通訊改成非同步:生產者把訊息丟進佇列就回,消費者自己慢慢處理。
三大價值:
- 解耦(decoupling):生產者不需知道消費者是誰、有幾個;各自獨立部署/擴展。
- 削峰填谷(buffering):流量突增時,佇列當緩衝,消費者按自己節奏消化,不被打垮。
- 可靠性:消費者掛了訊息還在佇列裡,恢復後繼續處理(不丟任務)。
典型用途:非同步任務(寄信、轉檔)、服務間事件、日誌/串流(Kafka)、削峰下單。
保證語義(必考):
- At-most-once:最多一次,可能丟(不重要的可接受)。
- At-least-once:至少一次,可能重複——所以消費者要冪等(idempotent)。
- Exactly-once:剛好一次,最難、代價最高(通常靠去重 + 冪等近似達成)。
🎤 面試官追問:「訊息會重複消費,怎麼辦?」(讓消費者冪等——用唯一 ID 去重,重複處理也不出錯。這幾乎必問。)「訊息順序重要怎麼保證?」(同一 partition / key 內有序,跨 partition 無序——用 key 路由。)「消費跟不上生產怎麼辦?」(擴展消費者、看 backlog 監控、必要時丟到死信佇列。)
把積木拼起來:一個讀多寫多的服務
這四塊很少單獨出現,通常組合成一條鏈:
使用者 → DNS → 負載平衡器(L7) → 應用伺服器(無狀態, 多台)
│ 讀
├─► 快取(Redis) ──miss──► DB(分片 + 複本)
│ 寫(非同步)
└─► 訊息佇列 ──► 消費者 ──► DB / 其他服務
- 無狀態應用層 + LB → 水平擴展、高可用。
- 快取 → 擋住大部分讀,保護 DB。
- 分片 + 複本 → 寫入/容量靠分片,讀取/高可用靠複本。
- 訊息佇列 → 把耗時的寫/副作用非同步化,削峰解耦。
🎤 面試官追問:「讀多還是寫多,架構差在哪?」(讀多 → 加快取 + 讀複本;寫多 → 分片 + 佇列削峰。)「哪裡可能是瓶頸?」(順著鏈找:LB→應用→快取命中率→DB 分片熱點→佇列 backlog。)
一張表收束四大積木
| 積木 | 解什麼問題 | 關鍵取捨 | 面試必答 |
|---|---|---|---|
| 負載平衡 | 水平擴展、高可用 | L4(快) vs L7(智慧路由) | LB 自己別變單點;無狀態化 |
| 快取 | 降延遲、護 DB | 一致性 vs 速度 | cache-aside、雪崩/穿透/擊穿 |
| 分片 | 突破單機容量/寫入 | 均勻 vs 範圍查詢 | 分片鍵選擇、consistent hashing |
| 訊息佇列 | 解耦、削峰、非同步 | 可靠 vs 複雜度 | at-least-once + 冪等 |
回答框架口訣
任何系統設計題,都可以用這四塊當骨架:
- 估規模(QPS、資料量、讀寫比)→ 決定要哪些積木
- 應用層無狀態 + 負載平衡 → 先把水平擴展鋪好
- 加快取 → 擋讀、降延遲(講清楚 cache-aside 與失效)
- 資料層分片 + 複本 → 寫入/容量靠分片、讀取/高可用靠複本
- 用訊息佇列 → 把耗時/副作用非同步化,削峰解耦
- 每一步都講取捨 → 面試官要的是「為什麼選它、代價是什麼」
記住一句話:系統設計不是背架構圖,而是認得這四塊積木各自解什麼問題,再依需求組起來、講清楚取捨。 積木熟了,再大的題目也只是它們的排列組合。