Aman Kumar Singh

Write-through 透過同步寫入快取與資料庫來保持一致性,這使得寫入速度與資料庫一樣慢。Write-behind 則採取相反的做法:立即寫入快取並快速回應,然後稍後才以非同步方式持久化到資料庫。寫入變得快速,因為它們只接觸記憶體。缺點是您引入了一個已提交的寫入僅存在於 Redis 中的時間窗口,因此當機可能會導致資料遺失。這是效能最高且風險也最高的快取模式,在使用之前必須清楚了解您所做的權衡。

這是 Redis 大師班的第 11 部分,接續 read-through 與 write-through

Write-behind 的運作方式

在 write-behind(也稱 write-back)中,寫入會先到達 Redis 並立即返回。資料庫更新則會以非同步方式單獨進行,並與其他寫入一起批次處理:

  1. 應用程式寫入 Redis 並立即獲得確認。
  2. 該寫入會被排入待持久化的佇列。
  3. 背景處理程序會清空佇列並寫入資料庫,通常會將許多寫入批次合併成較少的資料庫操作。
// write is instant: only touches Redis
await redis.hset(`counter:${id}`, 'value', newValue);
await redis.rpush('write_queue', JSON.stringify({ id, value: newValue }));
// a background worker later batches these into the database

Enter fullscreen mode Exit fullscreen mode

應用程式看到的寫入延遲僅為 Redis 的寫入時間,達到毫秒以下。資料庫稍後才吸收這些寫入,且透過批次處理能將許多小寫入轉換成較少的大寫入,同時降低資料庫負載。

為什麼它快,以及為什麼有風險

速度的提升來自兩點:寫入只接觸記憶體,以及批次處理能分攤資料庫的開銷。對於寫入密集的工作負載(高頻率計數器、指標、遙測、瀏覽追蹤),write-behind 可以吸收原本會同步壓垮資料庫的寫入速率。

風險同樣真實。在已確認的寫入與最終持久化到資料庫之間,資料僅存在於 Redis 中。如果 Redis 在此窗口期間當機(或持久化工作程序死亡),即使應用程式被告知寫入成功,這些寫入仍然會遺失。Write-behind 降低了耐用性:您告訴使用者「已儲存」,但實際上尚未真正持久化。對於某些資料來說這是可接受的,但對於其他資料則不可接受,而誠實地評估哪些資料適用是至關重要的。

什麼情況適合使用 write-behind

此模式適用於寫入吞吐量很重要,且偶爾遺失最近幾次的寫入是可以容忍的資料:

  • 分析與指標:頁面瀏覽、事件計數、遙測。偶爾當機遺失幾秒的計數並不重要,且寫入量很高。
  • 聚合計數器:按讚、瀏覽次數等,計數是累計值,小幅誤差可以自我修正。
  • 活動追蹤:上次出現時間戳、非關鍵日誌。

對於這些用途,write-behind 的吞吐量是真正的優勢,而耐用性的弱點是可以接受的權衡。共同點是資料量大且單筆資料的重要性較低。

什麼情況不適合使用 write-behind

此模式對於任何遺失寫入會造成嚴重問題的資料都是危險的:

  • 金融交易、訂單、付款:絕對不要對這些使用 write-behind。「已儲存」卻消失會是嚴重的錯誤。
  • 任何告知使用者已永久儲存,且遺失會造成驚慌的資料。
  • 沒有其他真實來源的資料:如果 Redis 是唯一存在寫入的地方,其遺失就是完全的損失。

規則是:如果當機時遺失最近幾秒的寫入會造成真正的意外事件,就不要使用 write-behind。請改用 write-through 或搭配同步資料庫寫入的 cache-aside,並接受較高的延遲作為耐用性的代價。

如何讓它更安全

如果您使用 write-behind,以下幾項措施可以降低(但無法消除)風險:

  • 啟用 Redis 持久化(AOF,稍後會介紹),讓 Redis 在重新啟動後可以恢復最近的寫入,縮小遺失窗口。
  • 使用耐用的佇列來存放待處理的寫入,例如 Redis stream,讓待處理寫入清單比純記憶體結構更能倖存。
  • 保持刷新間隔短,讓未持久化的資料窗口保持較小。
  • 監控佇列深度,讓您知道持久化是否落後,這同時會增加風險窗口並發出問題警訊。

這些措施可以讓 write-behind 風險降低,但無法使其達到同步資料庫寫入的耐用程度。窗口縮小了,但並未完全關閉。

Write-behind 是一種專業工具:以降低耐用性為代價,換取高容量且可容忍遺失資料的最大寫入速度。請刻意用於指標、計數器和遙測等吞吐量重要且偶爾遺失寫入無害的場景,並遠離任何絕對不能遺失的資料。這個決定完全取決於資料的價值,請有意識地做出決定。

接下來,我們將介紹 refresh-ahead 快取,它解決的是另一個問題:透過在熱門鍵過期前刷新來完全避免快取未命中。

重點摘要

  • Write-behind 會寫入 Redis 並立即確認,再以批次方式非同步持久化到資料庫。
  • 它提供毫秒以下的寫入,並透過批次處理降低資料庫負載,適合高寫入量的工作負載。
  • 風險在於耐用性:已確認但尚未持久化的寫入,如果 Redis 在此窗口期間當機就會遺失。
  • 適用於分析、指標和計數器等吞吐量重要且可容忍遺失最近寫入的場景。
  • 絕對不要用於金融資料、訂單或任何遺失寫入會造成真正意外事件的資料;請改用 write-through。

常見問題

什麼是 write-behind(write-back)快取?

一種寫入模式:寫入先到達快取並立即返回,資料庫則以非同步方式在背景更新,通常以批次方式進行。這使得寫入快速,但會產生一個已確認的寫入僅存在於快取中的時間窗口。

Write-behind 與 write-through 有什麼不同?

Write-through 會同步更新快取與資料庫,因此寫入具有耐用性但速度較慢。Write-behind 只同步更新快取,稍後才更新資料庫,因此寫入快速,但如果快取在持久化前當機,資料可能會遺失。

什麼時候應該使用 write-behind 快取?

適用於高容量且可容忍遺失的資料,例如分析、指標、遙測和聚合計數器,此時寫入吞吐量很重要,且偶爾當機遺失最近幾秒的寫入是可以接受的。

什麼時候應該避免使用 write-behind?

適用於金融交易、訂單、付款,或任何告知使用者已永久儲存的資料。如果遺失寫入會造成真正的意外事件,請改用 write-through 或同步資料庫寫入。

如何讓 write-behind 更安全?

啟用 Redis 持久化(AOF)、將待處理寫入存放在耐用的結構如 Redis stream 中、使用短的刷新間隔,並監控佇列深度。這些措施可以縮小潛在遺失的窗口,但無法達到同步資料庫寫入的耐用程度。

延伸閱讀


本文最初發表於 amanksingh.com/blog/redis-write-behind

關於作者

Aman Kumar Singh 是位於印度諾伊達的團隊負責人暨資深軟體工程師,撰寫關於全端工程、系統設計,以及使用 TypeScript、Next.js、NestJS、PostgreSQL 和 Redis 建構生產級 SaaS 的文章。