設計高度可擴展 Web 應用的關鍵考量:從單選題看系統設計核心
深入解析可擴展性設計的正確與錯誤實踐
目錄
題目解析
在系統設計面試中,經常會遇到這樣的問題:哪些考慮因素對於設計高度可擴展的 Web 應用程式通常很重要?
讓我們透過分析每個選項,深入理解可擴展性設計的核心原則。
選項分析
讓我們逐一分析每個選項,理解為什麼有些是正確的,有些是錯誤的。
✅ 正確選項
1. Data Partitioning (Sharding) - 資料分片
為什麼正確?
資料分片是處理大規模資料的核心策略。當單一資料庫無法存儲所有資料時,我們需要將資料分散到多個資料庫。
實際應用場景:
Instagram 的照片儲存:
- 問題:數十億張照片,單一資料庫放不下
- 解法:按用戶 ID 分片
- Shard 1: 用戶 1-1000000
- Shard 2: 用戶 1000001-2000000
- ...以此類推
分片策略比較:
分片策略 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
範圍分片 | 查詢簡單 | 熱點問題 | 時序資料 |
雜湊分片 | 分佈均勻 | 範圍查詢困難 | 用戶資料 |
地理分片 | 低延遲 | 跨區查詢複雜 | 全球服務 |
3. Asynchronous Processing - 非同步處理
為什麼正確?
非同步處理讓應用能快速回應用戶,同時在背景處理耗時操作。
經典範例:
YouTube 影片上傳流程:
1. 用戶上傳影片 → 立即回應「上傳成功」
2. 背景處理:
- 轉碼成多種格式(480p, 720p, 1080p, 4K)
- 生成縮圖
- 內容審核
- CDN 分發
同步 vs 非同步的差異:
同步處理(不可擴展):
用戶等待時間: 5 分鐘
伺服器阻塞: 是
用戶體驗: 差
擴展性: 低
非同步處理(可擴展):
用戶等待時間: 5 秒
伺服器阻塞: 否
用戶體驗: 好
擴展性: 高
5. Eventual Consistency - 最終一致性
為什麼正確?
在分散式系統中,要求即時一致性會嚴重影響效能。最終一致性是一種務實的權衡。
實際案例:
Facebook 按讚功能:
- 你按讚 → 立即看到讚數 +1
- 朋友可能 2 秒後才看到更新
- 這種短暫不一致是可接受的
一致性模型比較:
模型 | 特點 | 延遲 | 使用場景 |
---|---|---|---|
強一致性 | 所有節點即時同步 | 高 | 銀行轉帳 |
最終一致性 | 最終會同步 | 低 | 社交媒體 |
弱一致性 | 不保證同步 | 極低 | 快取系統 |
❌ 錯誤選項
2. Fully Synchronous Write Operations - 完全同步寫入
為什麼錯誤?
完全同步寫入會造成嚴重的效能瓶頸,違背可擴展性原則。
反面教材:
錯誤做法:
1. 用戶發文
2. 等待寫入主資料庫 ✓
3. 等待同步到所有備份 ✓
4. 等待更新所有快取 ✓
5. 等待通知所有粉絲 ✓
→ 用戶等待 30 秒!
正確做法:
1. 用戶發文
2. 寫入訊息佇列
3. 立即回應用戶
→ 背景慢慢處理其他步驟
4. Vertical Scaling on a Single Server - 單一伺服器垂直擴展
為什麼錯誤?
垂直擴展有明顯的上限,無法實現真正的高度可擴展。
垂直擴展的限制:
問題一:硬體上限
CPU: 最多 128 核心
記憶體: 最多 6TB
成本: 指數級增長
問題二:單點故障
伺服器當機 = 服務全掛
問題三:地理限制
全球用戶都要連到同一台機器
延遲問題無法解決
6. Blocking All Read Operations During Writes - 寫入時阻塞讀取
為什麼錯誤?
這種做法會嚴重影響系統可用性和用戶體驗。
為什麼這是災難性的設計:
情境:電商網站更新商品價格
錯誤做法:
- 更新期間所有用戶無法瀏覽商品
- 每秒損失數萬筆潛在訂單
正確做法:
- 使用 MVCC(多版本併發控制)
- 讀寫分離
- 樂觀鎖定
🎯 可擴展性設計的核心原則
1. 無狀態設計(Stateless)
好設計:
- Session 存在 Redis
- 任何伺服器都能處理請求
- 容易水平擴展
壞設計:
- Session 存在伺服器記憶體
- 用戶被綁定到特定伺服器
- 擴展困難
2. 解耦合(Decoupling)
緊耦合系統:
A服務 → 直接呼叫 → B服務
問題: B掛了,A也掛
鬆耦合系統:
A服務 → 訊息佇列 → B服務
優點: B掛了,訊息會累積,A繼續運作
3. 快取優先(Cache First)
請求路徑:
1. CDN 快取(靜態資源)
2. Redis 快取(熱資料)
3. 應用層快取(計算結果)
4. 資料庫(最後手段)