詳解快取更新策略及如何選擇

2023-05-12 21:00:55

概述

快取更新是指在資料發生變化時,保持快取和資料庫的資料一致性的問題。如果快取和資料庫的資料不一致,會導致使用者看到過期或者錯誤的資料,影響業務邏輯和使用者體驗。

為了實現快取更新,我們可以採用以下四種方式其中的一種:

  • Cache Aside策略:應用程式直接與資料庫和快取互動,並負責維護快取的一致性

    • 查詢:先查詢快取,如果快取中沒有,則查詢資料庫,並將結果寫入快取
    • 更新:先更新資料庫,然後刪除快取或者更新快取
  • Read/Write Through策略:應用程式只和快取互動,而是使用快取與資料庫互動

    • 查詢:先查詢快取,如果快取中沒有,則快取從資料庫中載入資料,並寫入快取
    • 更新:先更新快取,再由快取同步更新資料庫
  • Write Behind 策略:應用程式只和快取互動。當有資料更新時,只更新快取,不直接更新資料庫,改為非同步的方式更新資料庫

  • Refresh-Ahead策略:應用程式只和快取互動,由後臺服務與資料庫互動

    • 查詢:只查詢快取
    • 更新:由後臺服務自動從資料庫中查詢最新的資料,並將資料寫入快取中,

    不同於以上三種,應用程式無需等待資料的重新整理,也無需自己去觸發資料的重新整理,而是後臺服務來完成這些操作

Cache Aside

Cache Aside策略上文已經介紹過了,它是通過應用層面來實現的,分為兩種場景:

  • Cache Aside查詢策略
  • Cache Aside更新策略

Cache Aside查詢策略

如下圖所示:通過程式碼查詢快取,快取命中則返回,如果沒有命中則查詢資料庫並設定值

Cache Aside更新策略

如下圖所示:通過程式碼更新快取,先更新資料庫,後更新快取

這種策略簡單易用,但是需要維護快取和資料庫的一致性,可能出現快取穿透或快取雪崩的問題,一般採用延遲雙刪來保證最終一致性

延遲雙刪

延遲雙刪是一種保證資料一致性的常用策略,它的基本思想是在更新資料庫後,先刪除快取,然後等待一段時間,再次刪除快取。這樣做的目的是為了防止在資料庫和快取主從同步的過程中,有其他請求查詢到舊的快取資料,並寫回到快取中,具體的流程如下:

  1. 更新資料庫資料
  2. 刪除快取資料
  3. 休眠一段時間,時間依據資料的讀取耗費的時間而定。
  4. 再次刪除快取資料

延遲雙刪的休眠時間是根據業務讀取資料平均耗時來設定的,目的是確保讀請求可以結束,寫請求可以刪除讀請求造成的髒資料的問題。一般來說,休眠時間可以設定為500毫秒左右,但具體還要根據實際情況調整。休眠時間設定過長會影響效能和實時性,設定過短會導致資料不一致的風險。

延遲雙刪的優點是簡單易實現,能夠提高資料的最終一致性。但是延遲雙刪的缺點也非常明顯:

  • 延遲雙刪不是強一致性,有等待環節,如果系統要求低延時,這種場景就不合適了
  • 延遲雙刪不適合「秒殺」這種頻繁修改資料和要求資料強一致的場景
  • 延遲雙刪的延時時間是一個預估值,不能確保資料庫和redis在這個時間段內都實時同步或持久化成功了
  • 延遲雙刪不能完全避免redis存在髒資料的問題,只能減輕這個問題,要想徹底解決,還需要用到同步鎖解決

Read/Write Through

Read/Write Through只與快取做互動,分為兩種場景:

  • Read/Write Through查詢策略
  • Read/Write Through更新策略

Read/Write Through查詢策略

如下圖所示:先查詢快取,如果快取沒有,由快取去資料庫查詢,而不是應用層,查詢後更新快取

Read/Write Through更新策略

如下圖所示:先更新快取,再由快取同步更新資料庫

Write Behind

Write Behind 策略是指在寫入資料時,只更新快取中的資料,然後建立一個非同步任務或者定時任務來批次更新資料庫中的資料。這樣,應用程式無需等待資料庫的響應,也無需自己去同步更新資料庫和快取,而是交由快取服務來完成這些操作,如下圖所示:

Refresh-Ahead

是指在讀取資料時,如果快取中的資料即將過期,則由CDC服務自動從資料庫中查詢最新的資料,並將資料寫入快取中,然後返回給應用程式。不同於以上三種,應用程式無需等待資料的重新整理,也無需自己去觸發資料的重新整理,而是交由CDC服務來完成這些操作。

Refresh-Ahead 模式的工作原理如下:

  • 當用戶端存取快取中的某個資料項時,首先檢查該資料項是否即將過期,如果是,則啟動一個後臺執行緒或服務去從資料來源中獲取最新的資料,並替換掉快取中的舊資料;同時返回給使用者端
  • 如果該資料還沒有即將過期,則直接返回給使用者端
  • 如果該資料項已經過期,則從資料來源中獲取最新的資料,並替換掉快取中的舊資料,並返回給使用者端新資料。

CDC

CDC,全稱為Change Data Capture。它是一種軟體設計模式,可以讓使用者檢測和管理資料來源的增量變化,並將這些變化應用到企業的下游環節。CDC 技術可以實時捕獲資料的變化,只需要很少的資源,而不是全量資料批次處理。CDC 可以幫助實現資料同步、資料倉儲載入、資料分析等場景。

CDC 的優點:

  • 提高資料存取的效能和效率,因為它避免了重複地查詢整個資料集,而只需要獲取增量資料
  • 提高資料一致性和可靠性,因為它可以及時地將資料來源的變化同步到下游系統,避免了資料過期或丟失的風險
  • 提高資料分析和洞察的能力,因為它可以實時地反映資料的狀態
  • 提高資料整合和轉換的靈活性和可延伸性,因為它可以適應不同型別和結構的資料來源和目標,支援多種場景和用例。

CDC 的應用場景:

  • 資料同步:可將資料來源中的變化同步到其他資料庫或資料儲存中,例如快取、搜尋索引、備份等。
  • 資料倉儲載入:可將資料來源中的變化載入到資料倉儲或資料湖中,支援離線或實時的資料分析和報告。
  • 資料分析:可將資料來源中的變化傳送到流式處理平臺或機器學習平臺中,支援實時或批次的資料處理和建模
  • 資料觸發:可將資料來源中的變化作為觸發器,啟用其他系統或服務中的業務流程或邏輯,例如通知、審計、驗證等

CDC 的實現方式有多種,其中比較成熟的開源專案就是Debezium。它為CDC提供了一套低延遲的資料流平臺支援多種資料庫。例如:MongoDB、MySQL、PostgreSQL、SQL Server、Oracle等等。使用Debezium監控資料來源,並使用Kafka作為訊息服務,將資料來源的變化作為事件傳送到快取。這樣,快取可以非同步地接收和處理資料變化,而不需要定期地查詢資料來源

四種策略的選擇

我們介紹了四種常見的快取更新策略:Cache AsideRead/Write ThroughWrite Behind CachingRefresh-Ahead。在實際應用時,應該結合具體業務和應用場景來選擇合適的快取策略,接下來我們通過對比效能、資料一致性、冗餘資料、程式碼複雜度、業務邏輯、可靠性這幾個點來說明:

策略 效能 一致性 冗餘資料 程式碼複雜度 業務邏輯 可靠性
Cache Aside 較高 較低 較少 較高 較複雜 較低
Read/Write Through 較低 最高 較多 最高 最簡單 最高
Write Behind Caching 最高 最低 較少 較低 較簡單 較高
Refresh-Ahead 次高 次高 較多 最高 較複雜 最高

注意:
Refresh-Ahead策略是假定無CDC的情況下進行對比的

效能

  • Cache Aside 的效能較高,它只在快取未命中時才存取資料庫
  • Read/Write Through 的效能較低,它在每次讀寫時都需要存取資料庫
  • Write Behind Caching 的效能最高,它只在快取未命中時才存取資料庫,而寫入操作是非同步的
  • Refresh-Ahead 的效能介於 Cache AsideWrite Behind Caching 之間,它只在即將過期時才存取資料庫,並且寫入操作也是非同步的

資料一致性

  • Cache Aside 的資料一致性較低,它只在快取未命中時才更新快取,而寫入操作則是直接更新資料庫並將快取中的資料刪除或更新
  • Read/Write Through 的資料一致性最高,它在每次讀寫時都更新資料庫和快取
  • Write Behind Caching 的資料一致性最低,它只在快取未命中時才更新快取,而寫入操作則是先更新快取,並在非同步更新資料庫,有較大的延遲。
  • Refresh-Ahead 的資料一致性介於 Read/Write ThroughCache Aside 之間,它保證了快取中的資料總是最新的,但是有一定的延遲

冗餘資料

  • Cache Aside 的冗餘資料較少,它只將經常存取的資料儲存到快取中
  • Read/Write Through 的冗餘資料較多,它需要將資料庫的所有資料都儲存到快取中
  • Write Behind Caching 的冗餘資料與 Cache Aside 相同,因為它也只將經常存取的資料儲存到快取中
  • Refresh-Ahead 的冗餘資料與 Read/Write Through 相同,它也需要將資料庫的所有資料都儲存到快取中

程式碼複雜度

  • Cache Aside 的程式碼複雜度較高,它需要同時與快取和資料庫互動,並處理可能出現的異常情況
  • Read/Write Through 的程式碼複雜度最高,它需要實現資料庫的讀寫介面
  • Write Behind Caching 的程式碼複雜度較低,它只需要實現簡單的快取操作,並在非同步執行資料庫寫入操作
  • Refresh-Ahead 的程式碼複雜度與 Read/Write Through 相同,他它需要實現資料庫的讀寫介面(關於這點可以使用Debezium)

業務邏輯

  • Cache Aside 的業務邏輯較複雜,它需要同時與快取和資料庫互動,且返回的資料是最新的
  • Read/Write Through 的業務邏輯最簡單,它只與快取互動,且返回的資料是最新的
  • Write Behind Caching 的業務邏輯較簡單,它也只與快取互動,且返回的資料是最新的,由於是非同步更新,所以比Read/Write Through要複雜一些
  • Refresh-Ahead 的業務邏輯較複雜,它會同時與快取和資料庫互動,需要處理可能出現的異常情況,且返回的資料有可能是舊的,也有可能是新的(關於這點也可以使用Debezium)

可靠性

  • Cache Aside 的可靠性較低,因為它將快取作為資料庫的輔助層
  • Read/Write Through 的可靠性最高,因為它將快取作為資料庫的代理層
  • Write Behind Caching 的可靠性較高,因為它將快取作為資料庫前置層
  • Refresh-Ahead 的可靠性與 Read/Write Through 相同,因為它也將快取作為資料庫的代理層