在現代高併發分散式架構中,快取(如 Redis、Memcached)與資料庫(如 MySQL、PostgreSQL)往往是協同作戰的黃金搭檔。

然而,許多工程師在處理快取讀寫時,往往面臨經典的一致性困境:

  • 寫入資料時,究竟該更新快取還是刪除快取?
  • 究竟該先操作資料庫還是先操作快取?
  • 網路延遲與高併發交織下,如何防止舊資料覆蓋新資料?

為了解決這些問題,分散式領域總結出了五大經典快取讀寫拓撲模式。本文將全景剖析這五大模式的架構設計、一致性代價與工程最佳實踐。


1. 五大快取讀寫模式全景矩陣

五大分散式快取讀寫模式拓撲圖展示 Cache-Aside(旁路快取:應用層主導)、Write-Through(直寫:快取同步刷 DB)與 Write-Behind(非同步批量回寫:極限寫入吞吐)三種模式的資料流動拓撲。PATTERN 01Cache-Aside 旁路快取應用服務 (Application)應用層親自調度讀寫① 讀快取Redis 快取未命中查 DBMySQL 主庫持久化儲存② 寫資料庫③ 成功後刪除快取 (DEL)核心特徵與適用• 最通用,防範髒寫入• 建議搭配 CDC 非同步淘汰PATTERN 02Write-Through 直寫模式應用服務 (Application)只面對快取中介層① 寫入快取層快取中介層 (Cache Layer)由外層組件封裝 DB 存取② 同步直寫 DBMySQL 資料庫核心特徵與適用• 資料強一致,無髒讀• 寫入延遲受限於 DB 速度PATTERN 03Write-Behind 非同步回寫應用服務 (Application)極致寫入,秒回客戶端① 純記憶體極速寫入Redis 記憶體層暫存變更・背景批量聚合② 背景非同步批量刷盤MySQL 資料庫 (批次寫入)核心特徵與適用• 吞吐量極高,降低 99% DB 負載• 當機存在極短資料丟失風險
PATTERN 01

Cache-Aside 旁路快取 (最通用)

1讀取流程:先查快取,未命中則查 DB 並回填快取。
2寫入流程:先寫資料庫更新,成功後刪除快取 (DEL)。
3最佳搭檔:搭配 Binlog CDC 非同步淘汰徹底解耦。
PATTERN 02

Write-Through 直寫模式 (強一致)

1存取介面:應用層只面對快取中介層,不直接操作 DB。
2同步直寫:快取元件收到寫入時,同步將資料寫入 DB 才回覆成功。
3代價權衡:保證無髒資料,但寫入延遲受底層 DB 速度約束。
PATTERN 03

Write-Behind 非同步回寫 (極限吞吐)

1純記憶體:應用層所有讀寫只在 Redis 記憶體中完成,超低延遲。
2背景批次:定時或累積批量變更,以 Batch SQL 一次性回寫 DB。
3適合情境:遊戲經驗值、即時觀看數、點讚等高頻時計數。
圖:五大分散式快取讀寫模式拓撲與權衡比較
快取模式讀取流程寫入流程應用層感知一致性保證
1. Cache-Aside應用層先查快取,應用層先寫 DB,應用層負責協調最終一致性
(旁路快取 - 最常用)未命中則查 DB 寫入再刪除快取快取與 DB 雙寫(可能存在極短延遲)
2. Read-Through應用層僅查快取,同 Write-Through應用層只對接快取最終一致性
(直讀模式)快取元件自動載入 DB或外部更新快取封裝底層 DB(依賴快取中介層)
3. Write-Through同 Read-Through應用層寫快取,應用層只對接快取強一致性
(直寫模式)快取元件同步寫 DB寫入延遲包含 DB(兩者同步寫入)
4. Write-Behind同 Read-Through應用層只寫快取,應用層極速寫入最終一致性
(Write-Back 非同步)快取非同步批量刷 DB存在當機丟資料風險(寫入吞吐量極致)
5. Refresh-Ahead應用層查快取,外部正常更新自動預測載入適合定期熱點
(預測載入模式)快取過期前自動預刷消除快取穿透延遲(需精確存取預測)

2. 深入 Cache-Aside 模式與一致性終極爭議

Cache-Aside 是當今工業界應用最廣泛的模式。其標準步驟如下:

  • 讀取(Read):
    1. 應用程式先查詢快取;若命中則直接返回。
    2. 若快取未命中,查詢資料庫取得資料。
    3. 將資料庫讀取到的資料寫入快取,並返回給客戶端。
  • 寫入(Write):
    1. 應用程式先更新資料庫。
    2. 資料庫更新成功後,刪除快取(Delete Cache)。

2.1 爭議一:為什麼是「刪除快取」而不是「更新快取」?

  • 防止併發更新覆蓋(Race Condition):若請求 A 和請求 B 同時發起更新,若採用「更新快取」,由於網路延遲,可能發生「A 先更新 DB -> B 後更新 DB -> B 先更新快取 -> A 後更新快取」,導致快取中殘留了 A 的舊資料!
  • 惰性計算節省資源:許多快取值是經過複雜 SQL 聚合計算生成的(如包含多張關聯表)。若每次資料庫變更都去重新計算一次快取,在「寫多讀少」場景下會造成嚴重的 CPU 浪費;採用刪除策略,只有在使用者真正發起讀取時才進行按需計算。

2.2 爭議二:為什麼是「先寫 DB,再刪快取」而不是「先刪快取,再寫 DB」?

若採用 「先刪快取,再寫 DB」,在高併發下極易產生永久髒資料:

  1. 執行緒 A 刪除快取。
  2. 執行緒 B 進來發起讀請求,發現快取未命中,去查詢資料庫(此時 A 尚未寫入 DB,B 讀取到舊值)。
  3. 執行緒 B 將讀到的舊值寫回快取。
  4. 執行緒 A 終於完成資料庫更新。
  5. 結果:快取中將永久殘留 B 寫入的舊資料,直到 TTL 過期!

3. 延遲雙刪與 Binlog CDC 非同步淘汰實戰

即便採用了「先寫 DB,再刪快取」,在極端併發讀寫競爭下仍有微小概率產生髒資料(讀未命中 -> 寫 DB -> 刪快取 -> 讀執行緒將舊資料寫入快取)。

3.1 延遲雙刪(Delayed Double Deletion)

func UpdateData(ctx context.Context, key string, data Entity) error {
    // 1. 先寫資料庫
    if err := db.Update(ctx, data); err != nil {
        return err
    }
    // 2. 第一次刪除快取
    redisClient.Del(ctx, key)

    // 3. 非同步睡眠一段時間 (例如 500ms,需大於主從同步與讀請求耗時)
    go func() {
        time.Sleep(500 * time.Millisecond)
        // 4. 第二次刪除快取 (徹底清除併發讀線程可能回填的舊快取)
        redisClient.Del(context.Background(), key)
    }()
    return nil
}

3.2 現代黃金標準:Binlog CDC 非同步解耦淘汰

為了消除應用層對刪除快取的維護負擔與延遲雙刪的 sleep 開銷,現代大型架構普遍採用 CDC(Change Data Capture):

Binlog CDC 變更捕獲與非同步快取淘汰架構圖 展示應用服務寫入 MySQL 主庫,CDC 引擎(Canal/Debezium)捕獲 Binlog 並發送至 Kafka 訊息佇列,快取淘汰 Worker 非同步冪等刪除 Redis 快取。CDC ASYNC INVALIDATION ARCHITECTUREMySQL Binlog CDC 非同步解耦快取淘汰流程01應用層服務純粹寫入 DB 主庫02MySQL 主庫寫入並生成 Binlog03Canal / Debezium即時解析 Binlog 事件04Kafka / MQ 佇列變更持久化與重試05淘汰 Worker ➔ Redis冪等 DEL,業務完全解耦
ASYNC CDC FLOW

Binlog CDC 快取淘汰步驟

1
應用服務寫入

應用層僅純粹更新 MySQL 主庫,不承擔快取淘汰責任,徹底消除業務複雜度。

2
Binlog 事件捕獲

Canal 或 Debezium 模擬 MySQL 備庫協議,即時捕獲 Row 級別變更日誌。

3
訊息佇列緩衝

將變更事件投遞至 Kafka / MQ,提供削峰、持久化重試與死信防禦機制。

4
Worker 冪等刪除

消費者 Worker 讀取訊息,精準執行 DEL key,並天然適應主從複製延遲。

圖:基於 MySQL Binlog CDC 與 Kafka 的非同步快取解耦淘汰架構
  • 核心優勢:應用層業務邏輯與快取解耦;透過 Kafka 保證刪除指令的可靠重試與死信防禦;天然適配 MySQL 主從延遲。

4. Write-Behind(非同步批量回寫)的極限效能

在需要極致寫入吞吐量(例如遊戲伺服器角色經驗值、文章即時觀看數、點讚數)的系統中,每次點讚都去打一次資料庫會瞬間讓資料庫連線池崩潰。

  • 運作機制:應用層所有的讀寫操作完全只在 Redis 記憶體中進行。
  • 背景批次刷盤:背景守護行程以固定間隔(如每 5 秒)或定量累積(如滿 1000 筆)將記憶體中的變更合併為單一批量 SQL(INSERT ... ON DUPLICATE KEY UPDATE)一次性回寫資料庫。
  • 代價權衡:將資料庫寫入壓力降低了 99%,但若 Redis 伺服器在刷盤前發生硬體掉電,會丟失過去 5 秒內的記憶體資料。

5. 架構選型總結

  • 通用 Web 業務、電商交易、帳戶資料:一律選用 Cache-Aside + Binlog CDC 非同步淘汰。
  • 高頻計數、即時互動、允許極微量丟失:採用 Write-Behind(非同步回寫)。
  • 熱點新聞、定期報表、防穿透:採用 Refresh-Ahead 預測載入。