把流量問題丟給「再加幾台機器」處理,是中小型團隊最常見、也最貴的誤判。機器加下去,瓶頸只會從應用層轉移到資料庫、從資料庫轉移到網路、最後變成一個沒人說得清楚是哪裡卡住的黑盒子。大流量架構真正要解的,是「如何讓每一層各自獨立擴展」,而不是無腦堆硬體。
從讀寫比例倒推架構決策
設計高併發系統前,先問三個問題:讀寫比例多少?資料一致性能容忍多久?熱點集中在哪?這三個答案會決定後面所有取捨。
如果是讀多寫少的場景(例如商品頁、文章頁),CDN 與多層快取是 CP 值最高的投資。靜態資源丟 CDN,動態頁面用邊緣快取加上短 TTL,後端再放一層 Redis 作為熱資料快取。這套組合可以把絕大多數請求擋在資料庫前面。
但快取是雙面刃。常見的坑包括快取雪崩(同時失效)、快取穿透(查不存在的 key)、快取擊穿(熱點 key 失效瞬間湧入)。實務上的處理方式是 TTL 加上隨機抖動、空值也快取一小段時間、熱點 key 用 singleflight 或互斥鎖回源。這些細節不做,加再多 Redis 都救不回來。
如果是寫密集場景(金流、即時訊息、IoT),重點就轉到訊息佇列與資料庫分片。把同步寫改成非同步寫,用 Kafka、RabbitMQ 或 NATS 之類的佇列削峰,下游 worker 依自己的處理速度消化。資料庫端則依業務鍵(user_id、order_id)做水平分片,避免單一節點變成瓶頸。
# 示意:寫入路徑改為先入隊,response 立刻返回
# 實際接 API 請參考所選佇列的官方文件
def create_order(payload):
validate(payload)
queue.publish("orders.created", payload)
return {"status": "accepted"}
這個轉換看起來簡單,但代表整個系統要接受「最終一致性」。前端 UI、客服流程、對帳邏輯都得跟著調整,不是後端改一改就好。
無狀態與可觀測性是擴展的前提
要能水平擴展,服務本身必須是無狀態的。Session 不能放在記憶體、檔案不能寫在本機磁碟、排程任務不能假設「只有一台機器在跑」。這些原則聽起來基本,但很多既有系統在第一次擴容時才發現處處違反。
無狀態化之後,部署、滾動更新、自動擴縮才有意義。Session 推到 Redis、檔案推到物件儲存、排程改用分散式鎖或專門的 scheduler 服務——這些都是「未來想擴展」就得先付的稅。
可觀測性則是另一個常被低估的投資。當系統由十幾個微服務組成,沒有 tracing 就無法知道一個慢請求卡在哪一段。建議至少做到三件事:結構化 log(JSON 格式、帶 request id)、metrics(RED 或 USE 指標)、distributed tracing(OpenTelemetry 是目前的共通標準)。出事時能在幾分鐘內定位問題,比事後加機器值錢得多。
我們的觀察
大流量架構沒有銀彈,所有選擇都是取捨。我們在報價時通常會建議客戶:先確認業務真的需要這個規模,再決定要不要付這筆複雜度的稅。一個每天幾千單的電商,把快取與資料庫索引調好,可能就足夠撐五年;硬上微服務、佇列、分片,只會讓三人小團隊每天忙著救火。先把可觀測性做好、把服務做成無狀態,剩下的等流量真的來了再說,往往是最划算的路線。
