快取機制 (Caching)

一句話定位

快取是讀取太慢或太貴時的解法。把熱資料放在快速記憶體裡,讓大多數讀取直接跳過資料庫。
取捨核心:速度 vs 過時 / 失效 / 故障

為什麼需要快取

場景 沒快取 有快取
讀取用戶資料 PostgreSQL ≈ 50ms Redis ≈ 1ms(50×
個人化 feed(多 join) 200ms 1ms
高峰 DB CPU 80% 降 70–80%

資料庫存在磁碟、查詢付磁碟代價;記憶體離 CPU 近得多。


在哪裡快取(四層)

1. 外部快取(External Cache) — 預設答案

獨立服務,透過網路存取(Redis / Memcached)。

2. CDN — 靜態媒體首選

地理上分散,把內容快取在靠近用戶的 edge server

維吉尼亞 origin → 印度用戶:250–300ms
CDN edge → 同區域用戶:20–40ms

3. 客戶端快取(Client-Side)

4. 行程內快取(In-Process)

直接在 application 行程的記憶體裡。


四種快取架構模式

模式 寫入端 讀取端 適用
Cache-Aside App 直寫 DB,delete 或 update cache App 先查 cache,miss 就查 DB 再 set cache 預設首選
Write-Through App 寫 cache → cache 同步寫 DB 同 cache-aside 讀必新鮮、可接受寫變慢
Write-Behind App 寫 cache → 背景非同步 flush DB 同上 高寫入吞吐、可接受最終一致
Read-Through 同 Write-Through cache 自己處理 miss CDN 本質就是這種

Cache-Aside(旁載快取)— 預設

讀取:app → cache?
        hit → return
        miss → app → DB → set cache → return
寫入:app → DB
       app → cache.delete(key)  ← 失效,不更新
如果你只記一種模式,就記 Cache-Aside。

它和語言、框架無關,所有快取都能實作。

Write-Through / Write-Behind / Read-Through


淘汰策略(Eviction Policy)

策略 規則 適用
LRU 移除最久沒被存取的 預設首選(90% 工作負載適合)
LFU 移除存取次數最少的 長期持續熱門(熱門影片、排行榜)
FIFO 移除最早進入的(不看存取) 極少用,可能踢掉熱資料
TTL 不是淘汰策略,是每個 key 的過期時間 必備,常和 LRU/LFU 搭配

常見快取問題(面試 deep dive 區)

Cache Stampede(快取雪崩 / Thundering Herd)

熱門 key 過期瞬間,大量請求同時 miss 直接打 DB。

12:01:00 整點 key 過期
→ 數千請求瞬間 miss
→ 同時打 DB
→ DB 過載連鎖故障

解法

  1. Request Coalescing(請求合併 / Single Flight)最有效
    只讓一個請求去重建快取,其他等它的結果
  2. Cache Warming — 熱門 key 過期前主動刷新
    ⚠️ 只在 TTL 過期模式有效;寫入失效模式無效

Cache Consistency(快取一致性)

最常被問。發生在 cache / DB 對同一資料回不同值時。

用戶改大頭照 → 寫進 DB → 舊值還在 cache → 其他用戶在 TTL 內看到舊大頭照

解法(根據資料新鮮度選)

Hot Keys(熱 key)

單一 key 流量遠超其他,把某個 Redis 節點打掛。

user:taylorswift 每秒幾百萬請求,整個 Twitter 個人資料瓶頸在一台 Redis

解法

  1. 複製熱 key 到多節點 — 分散讀負載
    ⚠️ 各副本 TTL 錯開,否則同時過期再觸發 Stampede
  2. 加行程內備援快取 — 極端熱門值不要每次都打 Redis
  3. Rate Limiting — 對特定 key 異常流量踩煞車

面試怎麼談快取(五步驟)

不要一上來就喊 cache —— 先確立為什麼需要

Step 1:確認瓶頸

「用戶個人資料查詢尖峰每秒 500 次,每查 30ms,這是瓶頸。」

Step 2:決定快取什麼

設計 key:user:123:profiletrending:posts:global

Step 3:選快取架構

「我用 cache-aside。讀取先查 Redis,miss 就查 DB 並回填,再回傳。」

靜態內容 → 加 CDN。極端熱 key → 加 in-process。

Step 4:設定淘汰策略

「Redis LRU + user profile 10 分鐘 TTL。用戶更新時立刻 invalidate。」

Step 5:說明缺點(取捨)

挑 1–2 個最相關的:

不要快取所有東西

Senior+ 面試官在意你知道何時不該快取。一個 index 設計良好的 DB 已經夠用的場景,不用硬加 cache。


自我測驗重點

問題 重點
Latency 排序 In-Process < Redis < CDN
Cache-Aside vs Write-Through 差異 誰主動寫 cache、寫入時序
Cache Stampede 最有效解 Request Coalescing
Eviction policy 錯誤敘述 FIFO 不是生產主流
Hot Key 解法 複製 / in-process / rate limit
五步驟 瓶頸 → 快取什麼 → 架構 → 淘汰 → 缺點