Redis 緩存的常用設計模式
Redis 緩存的常用設計模式:
一. 寫操作:
- 以 Redis 統一視圖爲準:先更新緩存,後更新數據庫。
- Write Through Pattern 直寫模式:
首先將數據寫入緩存,再將數據立即同步到數據庫。
- Write Behind Pattern 寫後模式:
首先將數據寫入緩存,再將數據異步的批量同步到數據庫。
- 寫操作不經過緩存。
- Write Around Pattern 繞寫模式:
數據直接寫入數據庫,不經過緩存。
二. 讀操作:
4)Read Through Pattern 讀穿透:
如果緩存未命中,緩存層自動從數據庫中獲取數據,然後將數據寫入緩存中,最終由緩存返回數據給應用程序。
緩存系統自動處理數據加載,使得數據的讀寫操作對應用更加透明,通常與 write Through 結合使用,這意味這所有的操作都會通過緩存層。
三. 讀寫操作:
5)Cache Aside Pattern 旁路模式:
緩存操作是由應用程序顯式控制的,開發者可以根據特定業務需求來自定義管理緩存數據,更加靈活可控。
一. Write Through Pattern
寫穿透模式 (直寫模式):在這種模式下,應用程序在寫數據時,首先將數據寫入緩存,然後再將數據立即寫入到數據庫,確保數據庫和緩存中的數據保持一致。
步驟:
- 應用程序發起寫操作。
2. 首先將數據寫入緩存。
3. 再將數據立即寫入到數據庫。
先更新緩存,再立即更新數據庫。
優點:
數據一致性:每次寫操作都要同時更新緩存和數據庫,保證了緩存和數據庫之間的數據一致性。
即時的數據訪問:由於緩存始終保持最新狀態,讀取操作可以立即從緩存中獲取最新的數據,提高了數據訪問的速度。
缺點:
寫操作延遲:對於寫操作頻繁的場景,每次寫操作都要同時更新緩存和數據庫,導致寫操作延遲。
資源消耗:緩存和數據庫的同步更新會消耗更多的計算和內存資源。
適用場景:
適用於對數據一致性要求較高,寫操作不頻繁的場景。
例如:電商平臺的訂單處理,當用戶下單時,訂單信息既寫入緩存,也同步寫入數據庫,保證了數據的實時性和一致性。
二. Write Behind Pattern
寫後模式:在這種模式下,應用程序在寫數據時,首先將數據寫入緩存,然後再將數據異步的批量寫入到數據庫。
步驟:
- 應用程序發起寫操作。
2. 首先將數據寫入緩存。
3. 再將數據異步的批量寫入到數據庫。
先更新緩存,再異步更新數據庫。
優點:
提高寫操作性能:寫操作首先發生在緩存中,通常比寫入數據庫快得多。
減輕數據庫負載:異步批量寫入數據庫,減少對數據源的即時寫操作。
提高響應時間:寫操作首先發生在緩存中,可以更快的響應寫請求。
缺點:
數據一致性問題:由於數據是異步寫入數據庫的,導致緩存和數據庫之間在一定時間內的數據不一致。
適用場景:
適用於寫操作遠多於讀操作,且對數據一致性****要求不高的場景。
例如:用戶行爲日誌收集,用戶在網站上的點擊行爲被記錄在緩存中,然後異步批量寫入到日誌數據庫。
三. Write Around Pattern
繞寫模式:在這種模式下,應用程序在寫數據時,直接將數據寫入數據庫,寫操作不經過緩存(寫數據繞過緩存),緩存僅用於讀取操作。
優點:
提高緩存效率:寫操作不需要同步到緩存,緩存不會應爲寫操作而頻繁的失效或更新。
提高內存利用率:防止那些不會再次被讀取到的數據佔用緩存空間,提高資源利用率。
缺點:
無法保障數據一致性:如果更新的數據同時存在於緩存和數據庫中,則會造成緩存和數據庫中的數據不一致。由於緩存數據沒有被及時更新,導致從緩存中獲取到髒數據。
適用場景:
適用於數據寫入****後很少被讀取的場景。
例如:對於數據備份操作直接寫入到備份存儲中,不經過緩存;或者是針對報告、歸檔信息的操作。
四. Read Through Pattern
讀穿透模式:在這種模式下,應用程序在讀數據時,首先直接對緩存發起請求 (先查緩存),如果緩存未命中 (緩存中不存在該數據),緩存中間件會自動觸發一個回源操作,從數據庫或其它數據源中獲取數據,然後將數據寫入緩存中,最終由緩存返回數據給應用程序。
步驟:
-
應用程序請求讀數據。
-
首先查詢緩存中是否有數據的鍵存在。
-
如果緩存命中 (緩存中存在該數據),則直接從緩存中獲取數據,返回給應用程序。
-
如果緩存未命中,緩存層會從數據庫中獲取數據。
-
將數據寫入緩存。
-
緩存返回新加載的數據給應用程序。
優點:
降低數據庫的負載:一旦數據被加載到緩存中,後續的讀取請求將直接從緩存中獲取數據,減少了對數據庫的直接訪問。
提高系統的性能和併發讀取能力:讀操作從緩存中進行,緩存的讀取速度快,從而提高了系統的性能。
缺點:
高併發請求下的數據不一致:連續兩次寫入請求,由於寫入操作存在先後順序問題,當數據被更新時,其它併發請求可能還在讀取緩存中的舊數據,導致數據不一致。
回源延遲:如果緩存未命中,回源操作會導致數據的獲取有一定的延遲,特別是當數據量較大時,延遲會更加明顯。
解決方案:
設置合適的緩存數據過期時間,採用適當的緩存數據過期策略和緩存淘汰策略確保緩存的有效性。
“定期刪除 + 惰性刪除” 策略:用於刪除過期的緩存數據。
內存淘汰策略:用於在內存不足時,選擇要淘汰的緩存數據。
適用場景:
適用於讀取頻繁、寫入較少,對數據一致性要求不高,對速度和性能要求較高的場景。
緩存中放的是當前在線用戶的活躍數據,例如遊戲中的換皮膚、換裝備,用戶登錄系統後,用戶的所有行爲在緩存中生成副本(統一視圖)。
五. Cache Aside Pattern
旁路緩存模式:在這種模式下,讀數據時先查詢緩存,緩存命中則直接返回數據;緩存未命中,則查詢數據庫,查詢成功後,更新緩存中的數據。
寫數據時先更新數據庫,更新成功後刪除緩存。
讀數據:
-
首先查詢緩存中是否有數據的鍵存在。
-
如果緩存命中,則直接從緩存中獲取數據,返回給應用程序。
-
如果緩存未命中,則從數據庫中查詢數據。
-
查詢成功後,將數據寫入緩存。
-
最後,將數據返回給應用程序。
寫****數據:
-
直接將數據寫入數據庫。
-
寫數據庫成功後,刪除緩存。
優點:
確保緩存中存放的是真熱點數據:只有在實際需要時,才加載數據到緩存,避免緩存中填充未使用或很少使用的數據,保證緩存中存放的是當前窗口的活躍數據。
內存佔用小:只緩存真正的熱點數據,減少緩存空間的浪費,更有效的利用緩存空間。
提高靈活性:緩存操作是由應用程序顯式控制的,開發者可以根據特定業務需求來管理緩存數據。
缺點:
代碼複雜性:需要額外的代碼邏輯去處理緩存的加載和失效。
數據一致性問題:由於緩存更新依賴於應用程序邏輯,如果處理不當,可能會導致緩存和數據庫之間的數據不一致。
適用場景:
適用於讀多****寫少,對數據實時性要求不高的場景。
例如:新聞內容展示、博客文章的閱讀。
如果緩存刪除失敗,設置****緩存過期時間兜底。---- 保證****最終一致性
一. 緩存數據的類型
1) 靜態緩存數據
例如:字典表,靜態緩存數據沒有時間窗口,即沒有設置過期時間。
- 動態的緩存
當前窗口的活躍數據,需要設置合適的緩存過期時間。
2. 過期時間的設置
建議:過期時間 <= 業務時間 — 續期
總結:
即使緩存刪除失敗了,這個緩存數據也是帶有過期時間的,採用 “定期刪除 + 惰性刪除” 的策略。
定期刪除:Redis 默認每隔 100ms 就隨機抽取一些設置了過期時間的 key,檢查其是否過期,如果有過期就刪除。 定期刪除可能會導致很多過期的 key 到了時間並沒有被刪除掉,此時就要用到惰性刪除。
惰性刪除:在你請求某個 key 的時候,redis 會檢查這個 key 是否設置了過期時間,並判斷是否過期了,如果過期就刪除。
所謂延時雙刪:
A 讀數據 --> 發現緩存失效了 --> A 讀數據庫 (假設讀到 5) --> 更新緩存(緩存中數據爲 5)
在 A 讀數據後到更新緩存的過程中,發生了:
B 寫數據 ----> 寫入數據庫 (數據庫中值被更新爲 6) ---> 刪除緩存
這個寫操作正好卡在 A 讀後到更新的過程中。
於是有人提出了延時雙刪:
先更新數據庫 --> 更新成功後,立刻刪除緩存 --> 延時後再刪除緩存
延時雙刪並沒有徹底解決問題,也帶來了數據延時一致性的窗口期。
所以增加延時雙刪反而使得問題更復雜了,還不如直接給緩存中的數據設置合適的過期時間,採用緩存淘汰策略兜底。
即使有第三方直接更新了數據庫,而不是通過請求進來更新的,用設置緩存數據過期時間兜底的方案仍然可以解決問題。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/apwlJ9ui-yLob0qgT7R2uA