關於JWT,可以說是分散式系統下的一個利器,我在我的很多項目實踐中,認證系統的第一選擇都是JWT。它的優勢會讓你欲罷不能,就像你領優惠券一樣。 ...
緩存更新策略
緩存更新是指在數據發生變化時,保持緩存和資料庫的數據一致性的問題。如果緩存和資料庫的數據不一致,會導致用戶看到過期或者錯誤的數據,影響業務邏輯和用戶體驗。
為了實現緩存更新,我們可以採用以下四種方式:
-
Cache Aside策略:應用程式直接與資料庫和緩存交互,並負責維護緩存的一致性
- 查詢:先查詢緩存,如果緩存中沒有,則查詢資料庫,並將結果寫入緩存
- 更新:先更新資料庫,然後刪除緩存或者更新緩存
-
Read/Write Through策略:應用程式只和緩存交互,而是使用緩存與資料庫交互
- 查詢:先查詢緩存,如果緩存中沒有,則緩存從資料庫中載入數據,並寫入緩存
- 更新:先更新緩存,再由緩存同步更新資料庫
-
Write Behind 策略:應用程式只和緩存交互。當有數據更新時,只更新緩存,不直接更新資料庫,改為非同步的方式更新資料庫
-
Refresh-Ahead策略:應用程式只和緩存交互,由後臺服務與資料庫交互
- 查詢:只查詢緩存
- 更新:由後臺服務自動從資料庫中查詢最新的數據,並將數據寫入緩存中,
不同於以上三種,應用程式無需等待數據的刷新,也無需自己去觸發數據的刷新,而是後臺服務來完成這些操作
Cache Aside
Cache Aside策略上文已經介紹過了,它是通過應用層面來實現的,分為兩種場景:
- Cache Aside查詢策略
- Cache Aside更新策略
Cache Aside查詢策略
如下圖所示:通過代碼查詢緩存,緩存命中則返回,如果沒有命中則查詢資料庫並設置值
Cache Aside更新策略
如下圖所示:通過代碼更新緩存,先更新資料庫,後更新緩存
這種策略簡單易用,但是需要維護緩存和資料庫的一致性,可能出現緩存穿透或緩存雪崩的問題,一般採用延遲雙刪來保證最終一致性
延遲雙刪
延遲雙刪是一種保證數據一致性的常用策略,它的基本思想是在更新資料庫後,先刪除緩存,然後等待一段時間,再次刪除緩存。這樣做的目的是為了防止在資料庫和緩存主從同步的過程中,有其他請求查詢到舊的緩存數據,並寫回到緩存中,具體的流程如下:
- 更新資料庫數據
- 刪除緩存數據
- 休眠一段時間,時間依據數據的讀取耗費的時間而定。
- 再次刪除緩存數據
延遲雙刪的休眠時間是根據業務讀取數據平均耗時來設置的,目的是確保讀請求可以結束,寫請求可以刪除讀請求造成的臟數據的問題。一般來說,休眠時間可以設置為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模式去實現的
Refresh-Ahead 模式的工作原理如下:
- 當客戶端訪問緩存中的某個數據時,首先檢查該數據是否即將過期,如果是,則啟動一個後臺線程或服務去從資料庫中獲取最新的數據,並替換掉緩存中的舊數據;同時返回給客戶端
- 如果該數據還沒有即將過期,則直接返回給客戶端
- 如果該數據項已經過期,則從資料庫中獲取最新的數據,並替換掉緩存中的舊數據,並返回給客戶端新數據
CDC
CDC,全稱為Change Data Capture。它是一種軟體設計模式,通過監測數據變更(新增、修改、刪除等)而對變更的數據進行進一步處理的一種設計模式。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
相同,因為它也將緩存作為資料庫的代理層