快取更新是指在資料發生變化時,保持快取和資料庫的資料一致性的問題。如果快取和資料庫的資料不一致,會導致使用者看到過期或者錯誤的資料,影響業務邏輯和使用者體驗。
為了實現快取更新,我們可以採用以下四種方式其中的一種:
Cache Aside策略:應用程式直接與資料庫和快取互動,並負責維護快取的一致性
Read/Write Through策略:應用程式只和快取互動,而是使用快取與資料庫互動
Write Behind 策略:應用程式只和快取互動。當有資料更新時,只更新快取,不直接更新資料庫,改為非同步的方式更新資料庫
Refresh-Ahead策略:應用程式只和快取互動,由後臺服務與資料庫互動
不同於以上三種,應用程式無需等待資料的重新整理,也無需自己去觸發資料的重新整理,而是後臺服務來完成這些操作
Cache Aside策略上文已經介紹過了,它是通過應用層面來實現的,分為兩種場景:
如下圖所示:通過程式碼查詢快取,快取命中則返回,如果沒有命中則查詢資料庫並設定值
如下圖所示:通過程式碼更新快取,先更新資料庫,後更新快取
這種策略簡單易用,但是需要維護快取和資料庫的一致性,可能出現快取穿透或快取雪崩的問題,一般採用延遲雙刪來保證最終一致性
延遲雙刪是一種保證資料一致性的常用策略,它的基本思想是在更新資料庫後,先刪除快取,然後等待一段時間,再次刪除快取。這樣做的目的是為了防止在資料庫和快取主從同步的過程中,有其他請求查詢到舊的快取資料,並寫回到快取中,具體的流程如下:
延遲雙刪的休眠時間是根據業務讀取資料平均耗時來設定的,目的是確保讀請求可以結束,寫請求可以刪除讀請求造成的髒資料的問題。一般來說,休眠時間可以設定為500毫秒左右,但具體還要根據實際情況調整。休眠時間設定過長會影響效能和實時性,設定過短會導致資料不一致的風險。
延遲雙刪的優點是簡單易實現,能夠提高資料的最終一致性。但是延遲雙刪的缺點也非常明顯:
Read/Write Through只與快取做互動,分為兩種場景:
如下圖所示:先查詢快取,如果快取沒有,由快取去資料庫查詢,而不是應用層,查詢後更新快取
如下圖所示:先更新快取,再由快取同步更新資料庫
Write Behind 策略是指在寫入資料時,只更新快取中的資料,然後建立一個非同步任務或者定時任務來批次更新資料庫中的資料。這樣,應用程式無需等待資料庫的響應,也無需自己去同步更新資料庫和快取,而是交由快取服務來完成這些操作,如下圖所示:
是指在讀取資料時,如果快取中的資料即將過期,則由CDC服務自動從資料庫中查詢最新的資料,並將資料寫入快取中,然後返回給應用程式。不同於以上三種,應用程式無需等待資料的重新整理,也無需自己去觸發資料的重新整理,而是交由CDC服務來完成這些操作。
Refresh-Ahead 模式的工作原理如下:
CDC,全稱為Change Data Capture。它是一種軟體設計模式,可以讓使用者檢測和管理資料來源的增量變化,並將這些變化應用到企業的下游環節。CDC 技術可以實時捕獲資料的變化,只需要很少的資源,而不是全量資料批次處理。CDC 可以幫助實現資料同步、資料倉儲載入、資料分析等場景。
CDC 的優點:
CDC 的應用場景:
CDC 的實現方式有多種,其中比較成熟的開源專案就是Debezium。它為CDC提供了一套低延遲的資料流平臺支援多種資料庫。例如:MongoDB、MySQL、PostgreSQL、SQL Server、Oracle等等。使用Debezium監控資料來源,並使用Kafka作為訊息服務,將資料來源的變化作為事件傳送到快取。這樣,快取可以非同步地接收和處理資料變化,而不需要定期地查詢資料來源
我們介紹了四種常見的快取更新策略:Cache Aside
、Read/Write Through
、Write Behind Caching
、Refresh-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 Aside
和 Write Behind Caching
之間,它只在即將過期時才存取資料庫,並且寫入操作也是非同步的Cache Aside
的資料一致性較低,它只在快取未命中時才更新快取,而寫入操作則是直接更新資料庫,並將快取中的資料刪除或更新Read/Write Through
的資料一致性最高,它在每次讀寫時都更新資料庫和快取Write Behind Caching
的資料一致性最低,它只在快取未命中時才更新快取,而寫入操作則是先更新快取,並在非同步更新資料庫,有較大的延遲。Refresh-Ahead
的資料一致性介於 Read/Write Through
和 Cache 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
相同,因為它也將快取作為資料庫的代理層