Redis-6-三種快取讀寫策略

羊37發表於2024-06-09
快取策略 描述 優點 缺點 適用場景
旁路快取 (Cache Aside Pattern) 服務端需要同時維護資料庫和快取,以資料庫的結果為準。
讀請求先查快取,沒有命中則查資料庫並更新快取。寫請求先更新資料庫,然後刪除快取。
適合讀多寫少的場景,快取命中率高 寫操作複雜,存在短暫的資料不一致風險 資料讀寫比例懸殊、讀操作頻繁的場景。
讀寫穿透 (Read/Write Through Pattern) 服務端將快取視為主要資料儲存,所有資料的讀寫都透過快取,快取服務負責將資料同步到資料庫 簡化了應用程式的職責,資料一致性高。 實現複雜,增加了快取服務的壓力。 資料一致性要求高,且讀寫操作頻繁的場景。
非同步快取寫入 (Write Behind Pattern) 與讀寫穿透類似,但寫操作不立即同步到資料庫,而是非同步批次更新資料庫 寫操作效率高,減輕了資料庫壓力。 存在資料丟失風險,資料一致性較低。 寫操作頻繁且對實時一致性要求不高的場景

1 旁路快取

Cache Aside Pattern(旁路快取)適合讀請求比較多的場景

Cache Aside Pattern 中服務端需要同時維繫 db 和 cache,並且是以 db 的結果為準。

1.1.1 寫

  • 先更新db
  • 直接刪除快取

1.1.2 讀

  • 先讀快取
    • 有,則從快取返回。
    • 沒有,從db中讀取返回。
  • 再將讀取的資料寫入快取

1.1.3 常見問題

1.寫,寫DB後咋是刪快取不是更新快取?

寫N次,只有最後一次資料的有效,前面寫的都會被覆蓋。

讀不到的時候自然會重建快取,我們直接刪除了,等讀的時候重建即可。

2.寫,可不可以先刪除快取,再更新DB?

不可以。

前面已經講了,我們更新的時候,db更新沒問題,但是快取呢不做更新,直接刪除。

現在的問題就是,更新db和刪除快取這兩個操作的先後問題。

假設我們資料庫中有個值name=yang,我們準備更新成yang37

2.1 (×)先刪除快取,再更新db。

這個情況,主要是考慮併發場景,在刪除快取 -> 更新db期間,db中的始終是舊資料。

如果執行緒2發生了讀取請求,會導致資料不一致。

  • 執行緒1:嘗試更新name從yang到yang37
  • 執行緒2:嘗試讀取name的值

image-20240609123026058

最終我們可以看到,快取中的資料還是舊資料yang,而db中是正確的yang37,資料不一致

問題的根源在哪裡,在於你執行緒2讀取快取是很快的,快取中沒值,必然觸發快取的重建。

  • 執行緒1的db更新完了,那就皆大歡喜,即左邊執行緒1整個期間沒有併發問題。
  • 執行緒1的db沒更新完,db中的必定是舊資料,重建的快取值也必定成舊資料,導致出問題。

注意圖最下面的兩個框框。最重要的是,在快取有效期內,你快取的一直是髒資料,如果這個快取沒過期時間,那麼快取中的資料始終有問題。

這個問題出現的機率大嗎?即左邊刪除快取 -> 更新db的期間能不能包裹住右邊邊。

image-20240609133539141

實際上,db查詢資料比更新資料快,這個情況很容易出現。

2.2 (√) 先更新db,再刪除快取。

接著上面的,來看下先更新db的。

image-20240609125453219

嗯,這裡,如果更新db -> 刪除快取期間有讀取請求,我們的執行緒2還是會讀取到髒資料。

但是,咱們最終快取中的資料不存在,下次查詢會觸發重建。

先刪除快取再更新db的方式,邏輯上就存在問題了,咱們這個影響程度小的多。

咱們只是在這期間有短暫的不一致問題,不會導致最終狀態下的資料不一致。

但是,還是可能有快取最終不一致的情況,就是剛好快取失效了,例如下圖。

image-20240609131314236

這裡的問題點是什麼,執行緒2的更新快取操作覆蓋了執行緒1的更新db+刪除快取的操作。

它必須要完整的包裹住執行緒1的更新操作和刪除操作。

  • 如果執行緒1更新操作線上程2查詢db的操作之前,那麼此時查詢到的資料已經被更新了,重建的快取值剛好是正確的。
  • 如果執行緒1的刪除操作發生線上程2的更新快取之後,那麼此時快取中的資料已經被刪除了,下次重建會成為正確的值,也符合我們的預期。

我們注意下哈,先決條件是執行緒2從db查到舊資料之後,執行緒1開始更新db了。

然後,必須完完整整的包裹住這整個操作,必須得下面這樣。

image-20240609132053259

即,哪怕我像下面這樣兩種情況,它也不會有問題,必須滿足上面包裹住的場景,才可能有問題。

image-20240609132534799 image-20240609132659946

實際上,db查詢資料比更新資料快,所以這個更新db+刪除快取的時間很難比查詢db+更新快取的時間長

別忘了我們還有併發+快取失效的背景,這所有因素組合在一起才可能有這個資料不一致的情況,所以出現問題的機率是很低的。

你說,咱們方案1中過期時間的場景都還沒討論呢。哈哈,其實不用討論了,你想啊,方案1還沒說過期就有很大隱患了,還不要說咱們現在這裡的討論的方案2+過期的場景。

綜上呢,咱們的正確方案是:先更新db,再刪除快取。

1.2 讀寫穿透

Read/Write Through Pattern 中服務端把 cache 視為主要資料儲存,從中讀取資料並將資料寫入其中。

cache 服務負責將此資料讀取和寫入 db,從而減輕了應用程式的職責。

1.3 非同步快取寫入

Write Behind Pattern 和 Read/Write Through Pattern 很相似,兩者都是由 cache 服務來負責 cache 和 db 的讀寫。

讀寫穿透是同步更新 cache 和 db,而非同步快取寫入則是隻更新快取,不直接更新 db,而是改為非同步批次的方式來更新 db。

相關文章