【從一台 Server 到分散式架構】第 31 篇:從面試題反推架構——用本系列思維拆解系統設計考題

走完這三十篇,小明回頭看了一眼自己一開始問的問題:

「什麼時候該用 Redis?什麼時候要加 Kafka?為什麼要有 API Gateway?」

他發現,現在他的問題不一樣了:

「這個系統的瓶頸在哪裡?讀多還是寫多?要強一致還是最終一致?要即時還是可以非同步?」

這個思維方式,也是系統設計面試考的核心。


系統設計面試考的是什麼

面試官問「設計一個 XXX 系統」,考的不是:

  • ❌ 你背了多少架構圖
  • ❌ 你知不知道 Zookeeper 是什麼
  • ❌ 你能不能畫出完整的架構

考的是:

  • 你會不會從需求推導架構
  • 你知不知道每個設計決策的取捨
  • 你能不能清楚溝通你的思考過程

好的面試表現,是讓面試官跟著你的思路走,而不是讓他看你默寫答案。


可重複使用的面試框架:五個步驟

步驟一:釐清需求(5 分鐘)

問題比答案更重要。

不要拿到題目就開始畫架構。先問清楚:

功能需求(Functional Requirements)

  • 核心功能是什麼?(這個系統要做什麼)
  • 有哪些使用者角色?
  • 有沒有特殊功能?(例如:訊息系統要支援群組嗎?要有已讀回條嗎?)

非功能需求(Non-functional Requirements)

  • 預期規模?(多少用戶、多少 QPS)
  • 讀多還是寫多?
  • 對延遲的要求?(需要即時嗎?)
  • 對一致性的要求?(可以接受短暫不一致嗎?)
  • 可用性要求?(99.9% 還是 99.99%)

釐清需求,是避免「設計了一個沒人要的系統」的第一步。

步驟二:估算規模(3 分鐘)

拿到規模需求後,做一些 back-of-envelope 估算:

  • DAU(每日活躍用戶)× 每人每天的操作次數 → 每日請求總量
  • 每日請求 / 86400 秒 → 平均 QPS
  • 峰值 QPS ≈ 平均 QPS × 3(保守估計峰值倍數)
  • 儲存量:每條記錄多大 × 每天幾條 × 幾年

這個估算不需要精確,目的是感受「這個系統大概處於什麼量級」,決定需不需要分散式、需不需要快取、需不需要 Sharding。

步驟三:定義 API(3 分鐘)

把核心功能轉成 API 定義:

POST /messages          建立訊息
GET  /messages?room_id  讀取訊息

API 定義幫你鎖定「這個系統真正需要支援什麼操作」,避免設計過頭。

步驟四:高層架構設計(10 分鐘)

從最簡單的架構開始,逐步加入必要的元件。

先畫最小可行架構

[Client] → [API Server] → [Database]

然後問自己:「這樣夠嗎?哪裡會先出問題?」

根據需求和規模估算,逐步加入:

  • 讀多?→ 加 Cache(第 05 篇)
  • 寫多?→ 讀寫分離(第 06 篇)
  • 需要即時?→ WebSocket + Pub/Sub(第 17 篇)
  • 有耗時任務?→ 非同步 + MQ(第 07-08 篇)
  • 流量很大?→ Rate Limiting + 降級(第 13 篇)
  • 資料量很大?→ Sharding(第 15 篇)
  • 有搜尋需求?→ Elasticsearch(第 16 篇)
  • 需要全球低延遲?→ CDN(第 26 篇)

步驟五:深挖關鍵設計(15 分鐘)

面試官通常會選一、兩個最有趣的地方深問。

常見的深入方向:

  • 資料庫選型:為什麼用關聯式?為什麼不用 NoSQL?
  • 快取策略:快取的 key 怎麼設計?怎麼失效?
  • 一致性 vs 可用性:在 CAP 裡你傾向哪邊?為什麼?
  • 瓶頸在哪:這個架構的最脆弱點是什麼?怎麼處理?
  • 擴展策略:如果流量 10 倍,哪裡要改?

每個設計決策,都要說清楚「為什麼這樣選」和「代價是什麼」。


示範題一:設計 URL 短網址服務(TinyURL)

需求釐清

  • 輸入長網址,回傳短網址
  • 點擊短網址,跳轉到原始長網址
  • 短網址要永久有效(或設定過期時間)
  • 預期:每天 1 億次短網址點擊、100 萬次新建短網址

規模估算

  • 讀寫比 ≈ 100:1(讀遠多於寫)
  • 平均 QPS:100M / 86400 ≈ 1200 QPS
  • 峰值 QPS:約 4000 QPS
  • 儲存:1M 條 / 天 × 365 天 × 5 年 = 18 億條;每條 ~100 bytes → 180 GB

核心設計

短網址生成

把長網址映射到一個 6 位的短 ID。

做法:用自增 ID 轉 Base62(26 小寫 + 26 大寫 + 10 數字 = 62)。6 位 Base62 = 62^6 ≈ 568 億種組合,夠用。

讀取(重定向)

GET /{short_id}
→ 先查 Redis 快取
→ 沒有 → 查 DB
→ 回傳 301 / 302 Redirect

301(永久重定向)vs 302(臨時重定向):

  • 301:瀏覽器會快取,之後不再打到我們的伺服器——節省流量,但沒辦法追蹤點擊次數
  • 302:每次都打到伺服器——可以追蹤點擊,但流量多

取捨:要分析點擊行為 → 用 302;只要重定向功能 → 用 301。

取捨說明

決策理由
Redis 快取讀多寫少,熱門短網址反覆被點,快取命中率高
Base62 而非 MD5MD5 碰撞需要解決;自增 ID 簡單、唯一
302 而非 301大多數商業服務需要點擊追蹤

示範題二:設計即時訊息系統(Slack / Line)

需求釐清

  • 用戶之間可以一對一傳訊息
  • 有群組聊天室
  • 訊息要即時顯示
  • 用戶離線時,重新上線後要能看到歷史訊息
  • 預期:5000 萬 DAU,每人每天送 20 則訊息 → 10 億條訊息 / 天

規模估算

  • 平均 QPS:10 億 / 86400 ≈ 11,500(寫)
  • 加上讀取,峰值 QPS 約 5 萬
  • 儲存:每條訊息 ~1KB,10 億 / 天 → 1 TB / 天

核心設計

訊息傳送流程

A 送訊息
→ API 服務寫入 DB(訊息 ID 用 Snowflake,保證時序)
→ 寫入 Message Queue(通知要推送)
→ Worker 消費佇列
→ 查出 B 的 WebSocket 連線在哪台 WS Server
→ 透過 Pub/Sub 通知那台 WS Server
→ WS Server 推送給 B

離線訊息

B 不在線時,訊息存在 DB 裡。B 上線後:

  1. 建立 WebSocket 連線
  2. 查詢「上次上線後的所有未讀訊息」
  3. 前端顯示未讀

訊息儲存的選型

訊息的讀寫特性:

  • 寫入量大(高頻)
  • 讀取模式:主要是「某個聊天室最近的訊息,按時間排序」
  • 不需要複雜的關聯查詢

適合:Cassandra 或 HBase(寫多、按時間排序查詢、水平擴展容易),而不是關聯式 DB(複雜 Join 在這裡沒用到,而且寫入能力有限)。

取捨說明

決策理由
Cassandra 而非 PostgreSQL寫入量大,Cassandra 寫入效能好;查詢模式簡單(按 room_id + time 查)
Snowflake ID分散式環境下唯一且有序,訊息排序靠 ID 就夠
WebSocket + Pub/Sub即時通知(第 17 篇),多台 WS Server 用 Pub/Sub 路由

面試時常犯的幾個錯誤

錯誤更好的做法
拿到題目不問就開始畫先花 5 分鐘問清楚需求和規模
一開始就設計超複雜架構從最小可行架構開始,按需求逐步加複雜度
只說「要用 Kafka」,不說為什麼說清楚用 Kafka 解決什麼問題,代價是什麼
面試官問問題時死守原設計系統設計沒有唯一答案,面試官想看你怎麼回應新資訊
沉默自己想,不說出來把思考過程說出來,讓面試官跟著你走

這個系列學到的核心思維

走完這三十一篇,小明的思維方式已經不一樣了。他學會問:

當系統遇到問題時

  • 瓶頸在哪裡?CPU?記憶體?網路?DB?
  • 是讀的問題還是寫的問題?
  • 需要強一致還是最終一致就夠?

當要引入新工具時

  • 這個工具解決了什麼具體的問題?
  • 現有工具能不能解決?
  • 引入的代價(學習、維運、複雜度)值得嗎?

當面對高流量時

  • 可以快取嗎?(把重複計算省掉)
  • 可以非同步嗎?(把耗時任務移出主路徑)
  • 可以降級嗎?(非核心功能犧牲,保住核心)
  • 可以限流嗎?(不讓超出承受能力的量進來)

這套思維,不是用來背答案的,而是用來面對你沒見過的問題時,知道從哪裡開始想


系列終章

從第 01 篇的一前一後一 DB,到第 25 篇的完整分散式架構,小明的系統走過了:

負載均衡 → 快取 → 讀寫分離 → 非同步佇列 → 微服務 → 限流降級 → DB 分片 → 多樣化儲存 → 即時通知 → 分散式協調 → 監控追蹤 → 容器化 → Kubernetes → AI 助理

每一步都是因為有真實的問題,才引入真實的解法。

架構不是設計出來的,是長出來的

當你下次遇到一個系統設計問題,不管是工作中的真實需求,還是面試中的假設情境,希望你能想起小明從第一篇走到這裡的每一步:

從最簡單的起點出發,遇到問題才加複雜度,永遠知道每個決策的代價。

這就是系統設計。


【從一台 Server 到分散式架構】系列完。

相關推薦

2026-04-06
【從一台 Server 到分散式架構】第 30 篇:限流、排隊與降級實戰——開賣與直播場景
平常流量是 1000 QPS,但「開賣」的那一刻,瞬間湧入 50 萬個請求——系統要怎麼活下來?這篇用課程平台的限量課程開賣和直播開播場景,把第 13 篇學到的限流、降級、熔斷,落地到一個真實的高流量設計,走過每個防護層是怎麼工作的。
2026-04-05
【從一台 Server 到分散式架構】第 29 篇:用同樣的思維看 ChatGPT——AI 聊天系統架構
ChatGPT 看起來像一個聊天視窗,背後卻有幾個特殊的設計挑戰:回應是串流的、推理非常耗資源、每輪對話要記住上下文、系統要支援幾千萬用戶同時使用。這篇用熟悉的架構思維,拆解 AI 聊天系統的關鍵設計。
2026-04-04
【從一台 Server 到分散式架構】第 28 篇:用同樣的思維看 Twitter——社群動態與時序設計
Twitter 的核心功能看起來很簡單:發推文、看動態、按讚留言。但「動態牆」背後藏著一個棘手的設計問題:你追蹤 200 個人,每個人都可能隨時發文——你打開 App 時,那條時序動態要怎麼快速組出來?這篇來看社群動態系統的兩種策略:推(Fan-out on Write)與拉(Fan-out on Read)。
2026-04-03
【從一台 Server 到分散式架構】第 27 篇:用同樣的思維看 Uber——即時位置與派車系統
Uber 的核心問題看起來簡單:乘客在哪、司機在哪、把最近的司機派過去。但背後有幾個棘手的設計挑戰:海量的司機位置每秒更新、要在幾秒內完成司機配對、派單要公平且不重複。這篇用熟悉的架構思維,拆解 Uber 的即時調度系統。