寫入擴展 (Scaling Writes)

核心問題

寫入擴展往往比讀取更難。 讀取端工具(replica、cache)大家熟,但寫入是更大的挑戰:爆發性、高吞吐、又充滿競爭。面試官最愛問「黑色星期五訂單 4 倍怎麼辦」「跨年夜司機 3 倍怎麼辦」。


四個策略(組合運作)

1. 垂直擴展 + Database 選型     ── 先把單機/單 DB 壓榨到極限
2. Sharding 與 Partitioning     ── 水平分散負載
3. Queue + Load Shedding        ── 處理爆發流量
4. Batching + 階層式聚合        ── 改變寫入的「結構」

第一步:垂直擴展與 Database 選型

硬體天花板比你想的高

別預設「4~8 core + 機械硬碟」。200 CPU core、10Gbps 網路的機器並不罕見。先做 back-of-the-envelope 估算,確認真的碰到牆 → 詳見 02-Distributed-Systems/05-Numbers-to-Know

Database 選型反映你對寫入模式的理解:

Database 寫入特性 代價
Cassandra append-only commit log,順序寫 → 10k+ 寫入/秒(傳統 RDB 約 1k) 讀取差(要檢查多檔合併)
Time-series(InfluxDB/TimescaleDB) 為 timestamp 高頻順序寫設計 + delta encoding
Log-structured(LevelDB) append 而非 in-place
Column store(ClickHouse) 分析型批次寫入

通用優化:停用 FK constraint / trigger、調整 WAL flush 頻率、減少 index 數量。

讀寫之間的張力是寫入擴展的根本矛盾

優化寫入常犧牲讀取,反之亦然。先搞清楚瓶頸在讀還是寫,針對性優化。面試別只說「用更好的資料庫」,要解釋為什麼 Cassandra 的 append-only 比 MySQL 的 B-tree 更新快。


第二步:Sharding 與 Partitioning

Horizontal Sharding(分 row)

把資料分散到多個 shard,理想下 10 台 server 處理 10× 寫入。Redis Cluster 範例:key 經 CRC hash 得 slot number → 分配給不同 node。

最重要的任務:選對 Partition Key

好 key(如 hash(userID))→ 資料均勻分散,真正達到 10× server = 10× 效能。
爛 key(如「用戶國家」)→ 中國 shard 過載、紐西蘭 shard 閒置。
原則:選讓各 shard 寫入量差異最小的 key,通常 hash 主要識別碼。

同時考慮讀取:若每個讀取都要從所有 shard 收集資料,讀取 overhead 很大。問自己「這個請求要打幾個 shard?多常發生?」詳見 05-Database-Advanced/02-Sharding02-Distributed-Systems/04-Consistent-Hashing

Vertical Partitioning(分 column)

把不同存取模式的資料拆成專門的表,再移到不同 DB instance:

拆出的表 存取模式 優化方向
貼文核心內容 一次寫、多次讀 B-tree index、讀取優化
互動數字(讚/留言/觀看) 高頻寫入 in-memory / 專門計數器
分析資料 append-only time-series 行式壓縮

第三步:Queue 與 Load Shedding(處理爆發)

現實寫入流量不穩定。高峰 4× 代表平時只用 25% 容量 —— 大多數系統不這樣規劃。

Autoscaling 不是萬能藥

擴縮要時間,且 DB 擴展過程常需停機/降吞吐 —— 偏偏發生在業務最忙時。

Write Queue(Kafka / SQS)

解耦「接受寫入」與「處理寫入」,吸收爆發。DB 以穩定速率消化,queue 當緩衝。

Queue 會掩蓋底層問題

若 app 持續以比 DB 快的速度寫入,queue 無限成長、等待越來越久。只在爆發是短暫時用 queue,不要拿它 patch 連穩定負載都扛不住的 DB。詳見 09-Messaging-Coord/01-Data-Pipeline10-Design-Patterns/03-Long-Running-Tasks

Load Shedding(負載丟棄)

系統過載時,主動丟棄最不重要的寫入,保住重要的。Robotaxi 位置每幾秒更新一次 → 丟一筆,幾秒後就有更新鮮的;分析系統可暫丟 impression、保住 click。是「洩壓閥」,讓糟糕(過載)不演變成災難(崩潰)。


第四步:Batching 與階層式聚合

改變寫入結構。單筆寫入有 overhead(網路來回、transaction、index 更新),且 DB 批次寫入效率遠高於逐筆。

Batching(三層)

層次 做法 風險
應用層 client 攢一批再送 app 是 source of truth 時可能丟資料
中間處理層 Like Batcher 一分鐘窗口聚合 100 讚 → 1 次寫入
Database 層 調 flush 頻率(Redis 預設 100ms) 「大鎚」解法,留給極端情況
Staff 級深入

若大多數貼文每小時才一個讚,一分鐘批次根本沒幫助 —— 要確認 batching 真的帶來收益。

Hierarchical Aggregation(階層式聚合)

最極端情況(分析/串流)。關鍵:聚合視圖可遞增建立。直播留言例:幾百萬人寫、又要傳給所有人 →

大幅降低任一系統的寫入量,代價是增加延遲。


常見 Deep Dive


何時用 / 何時不用

✅ 適合 ❌ 不適合
Instagram(user ID sharding + vertical partitioning) 根本不需要擴展時 —— 先 back-of-the-envelope 估算
News Feed(名人寫入 fan-out) 每種策略都有取捨:Queue→延遲、partition→讀取複雜、batch→延遲
搜尋應用(寫入前重前處理) 最糟是製造出原本不存在的問題
直播留言(hierarchical aggregation 解 all-to-all)
核心洞察

寫入擴展 = 降低每個元件的吞吐壓力。不管是分散到 10 個 shard、用 queue 平滑爆發、還是批次成 100 次批量,都是同一原則:讓每個元件只處理可管理的負載。