分散式快取 (Distributed Cache)

一句話定位

Distributed Cache 的難點不在於快,而在於它自己變成一個需要被設計與維運的分散式系統
當 cache 從單機升級成 cluster,問題重心從「記憶體夠不夠」轉移到「key 怎麼分配、節點掛了怎麼辦、rebalancing 期間會不會打掛資料庫」。它不再只是加速層,而是一個獨立的 distributed system,必須處理 sharding、replication、failover、一致性、流量偏斜。

從單機到 Cluster:問題的重心轉移

單機 cache 的問題集中在記憶體管理與讀寫流程;一旦變成多節點 cluster,重心整個換掉。

維度 單機 Cache Distributed Cache
核心問題 eviction(LRU)、TTL、cache-aside vs write-through、stampede key 分配、節點故障、網路延遲、流量偏斜
容量上限 單機記憶體 水平擴展(加節點)
故障影響 整個 cache 沒了 部分 shard 失效,可 failover
本質 記憶體優化 獨立的分散式系統

典型觸發點:電商原本一台 Redis 存所有商品頁,流量成長到單機記憶體撐不住 → 升級 Redis Cluster。從這刻起,要回答的是「key 怎麼分配」「某台掛掉影響多大」「rebalancing 期間流量會不會暴增」。


Sharding 與 Consistent Hashing

升級成 cluster 後第一個必須解決的問題是 key 的分布

簡單做法 hash(key) % N:
  N=5 → key "user:123" 落在 node 2
  擴容成 N=6 → 同一個 key 落在 node 4   ← 幾乎所有 key 重新映射!

Consistent Hashing(hash ring):
       node A
      /      \
  node D      node B      加入 node E 只影響 D→E 之間的 key
      \      /             其他 key 位置不變 → 只有 1/N 失效
       node C
做法 擴容/縮容時的衝擊 適用
hash(key) % N 幾乎所有 key 重新映射 → 大量 miss 節點數固定、不會變動
Consistent Hashing 只影響一小部分 key(約 1/N) cache cluster 預設選擇
為什麼 % N 在高流量系統是災難

社交平台把 5 台節點擴容成 6 台,若用 % N,幾乎所有 profile cache 同時失效。接下來幾分鐘資料庫承受平常數倍流量 —— 等同瞬間 cold start。Consistent Hashing 把這個衝擊降到只影響部分 key。
但要記住:Consistent Hashing 只解決平均分布,不解決流量偏斜(skew)

詳見 02-Distributed-Systems/04-Consistent-Hashing,那裡有 virtual node 如何讓分布更均勻的完整推導。


Hot Key 問題

實務上最常見的不是平均負載,而是極端負載。熱門直播間、明星帳號、首頁推薦資料,單一 key 可能佔整體流量 20% 以上。

Consistent Hashing 救不了 Hot Key

即使 key 均勻分布在節點上,只要這個 hot key 對應到某一台機器,那台機器就成為 bottleneck。分布均勻 ≠ 流量均勻。

                 ┌─ node A (10% 流量)
   全部 key ─────┼─ node B (10% 流量)
   均勻分布      ├─ node C ★ 30% 流量 ← hot key "livestream:99" 全壓這台
                 └─ node D (10% 流量)

常見解法:

解法 做法 取捨
Key 多副本 同一 key 存多份(key#0~key#N),client 隨機讀 讀分散,寫要更新多份
Local in-process cache application server 本地再加一層快取 最快、省網路,但各機可能 stale
拆分邏輯 key user:123:profile 拆成 :0/:1/:2 分散到多節點 讀寫邏輯變複雜
Hot key 是「跑起來才知道」的問題

這類問題通常在流量真的變大之後才浮現,設計階段難以完全預測。所以 distributed cache 要能 scale(擴展),也要能應對 skew(分布不均)。面試提到 hot key 緩解策略會明顯加分。


Cache Replication 與 High Availability

很多團隊早期假設「cache 掛掉沒關係,反正能從資料庫重建」。在高流量系統中,這個假設會造成事故。

正常:50k QPS ─┬─ 45k 命中 cache
              └─ 5k 打 DB(DB 容量剛好夠)

cache cluster 全掛:
  50k QPS ──────────────→ 全部回源 DB
  DB 只有 5k capacity → 瞬間 10× 流量 → 雪崩

當 cache 成為資料庫前面的保護層,一旦整個 cluster 失效,所有請求直接回源。原本 90% 由 cache 處理 → 資料庫瞬間承受 10 倍流量。

因此 distributed cache 通常需要 replication:每個 shard 配一個 replica,primary 掛掉時 client 快速 failover。

對照 05-Database-Advanced/03-Replication:資料庫 replication 常需考慮 sync vs async、讀寫分離;cache replication 幾乎一律 async,因為丟資料的代價低得多。


Failure Modes(故障模式比資料庫更隱晦)

Distributed cache 的失效通常是性能退化,而不是明顯 crash —— 這讓它更難偵測。

Cache Stampede(驚群 / 雪崩)

大量 key 同時過期 → 同時 miss → 同時回源。distributed 環境會放大:多個 app server 同時 miss、多個 cache node 同時回源,資料庫壓力瞬間飆升。

TTL 全設 300s:
  t=0   寫入 1000 個 key,TTL=300
  t=300 全部同時過期 → 1000 個請求同時打 DB ← stampede

加 jitter(TTL=300 ± random(0~60)):
  過期時間分散在 t=240~360 → DB 壓力平滑
緩解 機制
TTL jitter TTL 加隨機抖動,避免 key 同時過期
Single-flight application 層確保同一時間只有一個請求去回源,其餘等結果

Cold Start

新節點加入 cluster 或整個 cluster 重啟時 cache 為空 → 大量 miss。夜間部署、自動擴容時很常見。緩解:新節點加入後先預熱,或限流逐步切換,避免瞬間把所有請求導到空節點。

Partial Node Failure(最棘手)

比起直接 crash,更常見的是某台 node 變慢但沒完全失效

node C 變慢 → client timeout → retry
  → retry 流量打到其他 node 或 DB → 連鎖效應 → 整體崩潰
部分節點退化比整台掛掉更危險

因為 client 還在對它發請求、timeout、retry,重試流量會擴散到健康節點和資料庫。client 端必須有 合理 timeout + 重試上限 + circuit breaker,避免單點性能退化演變成整體崩潰。這正是 01-Networking/07-Fault-Handling 的核心模式。


一致性問題

Cache 天生就是放寬一致性的設計。distributed 環境讓一致性管理更複雜。

Cache Invalidation

寫 DB 後廣播 invalidation 到所有 node:
  node A ✓ 收到 → 刪舊資料
  node B ✓ 收到 → 刪舊資料
  node C ✗ 漏收 → 持續回傳舊資料(永久 stale!)

單機 invalidation 已經很難,多節點下若有節點漏收訊息就會永久保留舊資料。

業界主流策略:TTL 為主,invalidation 為輔

即使 invalidation 失敗,資料最多 stale 一段「可接受時間」(TTL),而不是永久錯誤。用 TTL 當兜底,把一致性問題從「正確性」降級成「新鮮度」。

Write Path 與 Cache 同步

模式 寫入流程 多節點下的風險
Cache-aside 寫 DB → 刪 cache 刪除失敗 → 某些節點殘留舊資料
Write-through 同時寫 cache + DB 兩者一致性難保證(部分成功)

實務上團隊通常選簡單且可推理的模式(cache-aside + TTL),而不是追求強一致。寫入模式的細節見 07-Caching-Storage/01-Caching

Multi-Region:每區一套,不跨區複製

US Region          EU Region
┌──────────┐       ┌──────────┐
│ cache US │       │ cache EU │   ← 不跨 region 複製 cache
└────┬─────┘       └────┬─────┘
     └──── DB(跨區複製,source of truth)────┘

跨 region 時通常每個 region 有自己的 cache cluster,不跨區複製。理由很實際:cache 是可重建的資料,不值得承擔跨區 replication 的複雜度與延遲。EU 故障時流量切到 US 會出現短暫 cold cache,但資料仍在 DB 裡。強調區域隔離,而非全球一致的 cache。


面試話術

不要只說「用 Redis Cluster」

完整回答要展示你理解 cache 是 performance optimization,但 distributed cache 本身需要完整的分散式設計思維。涵蓋六個面向:① 為何要分散 ② key 如何分片 ③ 擴縮容如何影響命中率 ④ cluster 故障如何保護 DB ⑤ stampede / hot key 處理 ⑥ 是否需要 replication 與一致性要求。

一段式範例回答

「我們用 sharded distributed cache,透過 consistent hashing 分配 key,避免擴縮容時大量失效。每個 shard 配一個 replica,primary 掛掉時 client 快速 failover。為了防止 stampede,TTL 加入隨機 jitter,並在應用層做 single-flight,確保同一時間只有一個請求回源。對於 hot key,在應用層加本地快取或做 key-level replication。如果整個 cache cluster 出問題,系統會限制回源流量並啟動降級模式,保護資料庫。」


常見 Deep Dive


自我測驗重點

問題 重點
單機 vs distributed 重心差異 從記憶體管理 → 變成獨立的分散式系統
% N 為何不行 節點變動幾乎全部 key 重映射 → 大量 miss / cold start
Consistent Hashing 解什麼 只解平均分布,不解 流量偏斜(hot key)
Hot key 三招 多副本隨機讀 / 本地快取 / 拆邏輯 key
cluster 全掛的後果 所有請求回源 → DB 瞬間 N 倍流量 → 雪崩
Cache replication 一致性 eventual consistency 即可,cache 非 source of truth
Stampede 兩招 TTL jitter(源頭打散)+ single-flight(回源去重)
Partial node failure 為何危險 變慢非 crash → timeout/retry 風暴 → 需 circuit breaker
Invalidation 策略 TTL 為主、invalidation 為輔(漏訊息只造成短期 stale)
Multi-region cache 每區一套、不跨區複製;failover 容忍 cold cache