大流量架構最常見的災難,不是撐不住流量,而是還沒到那個量就先被自己的架構複雜度壓垮。在進入任何技術選型之前,先問一個問題:你的瓶頸到底在哪一層?沒有量測就上分片、上 Kafka、上多級快取,通常只是把一個問題換成三個更難 debug 的問題。
從讀寫比與一致性需求倒推
高併發系統的設計順序,應該是從業務特性開始,而不是從元件清單開始。
讀多寫少的場景(例如商品頁、文章、排行榜),重點在快取層。CDN 擋住靜態資源與可快取的 API 回應,應用層用 Redis 處理熱資料,資料庫只負責少量穿透請求。這類系統的工程重點,其實是 cache invalidation——什麼時候要清、清得乾不乾淨、舊資料容忍多久。
寫多或讀寫接近的場景(例如金流、庫存、即時訊息),快取的價值會大幅下降,重點轉移到資料庫本身。這時候才需要認真評估分片(sharding)、讀寫分離、或者把部分寫入路徑用訊息佇列削峰。
舉個例子,假設一家中型電商平常流量平穩,但每月有一次大型促銷會帶來十倍以上的瞬間寫入。這種情境下,把下單請求先寫入訊息佇列、由 consumer 平穩地處理庫存扣減與訂單建立,會比硬撐資料庫連線數合理得多。但代價是:使用者下單後不會立即看到訂單成立,UI 要處理「處理中」狀態,整個前後端的契約都要跟著改。架構決策從來不是免費的。
無狀態與可觀測性是底線
不管你選哪種架構,有兩件事幾乎沒有例外。
第一是服務無狀態化。session 放 Redis 或 JWT,檔案放物件儲存,本機只跑運算邏輯。這樣水平擴展才有意義,autoscaling 才不會在擴出新節點後因為 session 不一致而出包。
第二是可觀測性。high-traffic 系統一旦出事,靠 log grep 是來不及的。最低標準是三件套:結構化日誌、metrics(至少 RED:Rate、Errors、Duration)、以及跨服務的 trace。下面是一段示意,講的是給 HTTP handler 加上基本 metric 的骨架:
# 示意:實際接法請參考 OpenTelemetry / Prometheus client 官方文件
from time import perf_counter
def observed(handler):
def wrapper(req):
start = perf_counter()
try:
resp = handler(req)
record_metric(handler.__name__, 'ok', perf_counter() - start)
return resp
except Exception:
record_metric(handler.__name__, 'error', perf_counter() - start)
raise
return wrapper
重點不是這幾行程式,而是「每一個關鍵路徑都要能被量到」。沒有 metric,你做的所有效能優化都只是猜測。
另外一個經驗法則:在報價或設計階段,我們通常會建議客戶先把可觀測性、CI/CD、與基礎的快取層做扎實,再去談分片與訊息佇列。前者是地基,後者是加蓋。地基沒打,蓋幾層都會塌。
我們的觀察
大部分中小型系統的真實流量,距離需要「大廠級架構」還有很長一段距離。把工程資源投在量測、快取、與資料庫索引調校上,通常比急著導入分散式元件帶來更穩定的效益。架構複雜度本身就是成本,能用一台機器解決的問題,先別急著用十台。
