項目中用到了Redis做緩存,如何保證數據庫和緩存的數據一致性?

95後程序猿小A


先舉兩個例子,都是我做過的項目,應用場景都非常簡單:

  • 曾經做過的一個保費試算的項目,業務場景是根據用戶基本信息+費率做計算,得到一個保費值,如果不瞭解這個業務場景的話,可以看做【用戶輸入】+【數據庫數據】+【一定的計算】=【結果】;這些數據複雜而且多,使用頻繁,但是每個產品的費率數據,幾乎是不會改變的(新增修改刪除都不會有):這些數據被我們放在Redis中,而且設置成永不失效;因為數據不會修改,那麼數據庫和緩存中的數據肯定是一樣的。

  • 現在做的項目中的一個功能,簡單描述業務場景:系統從N個業務系統中抽取數據,並做加工整合,加工完成後更新到數據庫中;這個場景也比較有特點,數據時效性要求很低(數據加工到數據庫已經晚了一天),數據變化的時間固定且唯一(批處理每天固定時間進行,只有一個批處理線程在跑);於是我們採用了一個比較保守的策略,就是設置數據在Redis中的過期時間;等數據過期後,當查詢的時候發現數據不在緩存中,再從數據庫中查詢出來後放入緩存。

現在想一想Redis中數據的更新策略有哪些(或者說緩存更新策略)?

  • 給緩存設置過期時間,是我認為比較好的方式(這裡不討論緩存雪崩的問題),這個【比較好】是在實現難度和數據一致性之間找到了一個平衡點;實現起來非常簡單,但是可能在數據變化後,緩存失效前的這段時間,數據庫和緩存之間的數據是不一致的(但最終會一致)。
  • 先更新數據庫,再更新緩存:有兩個比較大的問題,一是多線程的時候,可能會造成數據庫和緩存數據的不一致,而且這個不一致可能會是長期的。二是如果更新操作比較多的話,會頻繁地更新緩存。

  1. A線程:set 數據庫 key=1;
  2. B線程:set 數據庫 key=100;
  3. B線程:set 緩存 key=100;
  4. A線程:set 緩存 key=1;(實際上,應該等於100,這樣就造成了緩存和數據庫中的數據不一致,並且如果數據不再更新,那麼會使長期不一致)

  • 先刪緩存,再更新數據庫:這個會出現問題,還是A/B兩個線程,如果A刪除了緩存之後,在更新數據庫之前,B過來查詢不到緩存,於是就查詢了數據庫(A還沒更新),把數據寫入緩存,這時候A才把數據庫更新掉,這可能就會造成永久性的不一致。

  • 先更新數據庫,再刪緩存:一樣的道理,大家可以自己想一想這個過程。

有些人可能覺得這個概率很小,怎麼可能有這麼巧的事情,恰好都是A做到一半,B插進來把全部的事情做完,A再去做後一半的操作;但是概率小,也是可能發生的,不得不考慮。

還有很多複雜的實現方案,比如更新完數據庫之後,把更新操作發給有序的消息隊列(不過過程越複雜,不一致的時間會越長),由另外的線程從隊列裡面順序執行更新或刪除緩存的操作;

不過我認為,架構越複雜也越容易出錯,還不如根據實際的場景和團隊的開發水平,合理選擇緩存的使用,並給緩存設置合理的過期時間。

我將持續分享Java開發、架構設計、程序員職業發展等方面的見解,希望能得到你的關注。


會點代碼的大叔


1 不建議緩存和數據庫強一致

(1) 在緩存作為提升系統性能手段的背景下,不需要保證數據庫和緩存的強一致性。如果非要保證二者的強一致性,會增大系統的複雜度

(2) 如果更新數據庫成功,再更新緩存。此時存在兩種情況:更新緩存成功則萬事大吉。更新緩存失敗,沒有關係,等待緩存失效,此處要合理設置失效時間

(3) 如果更新數據庫失敗,則操作失敗,重試或者等待用戶重新發起

(4) 數據庫是持久化數據,是操作成功還是失敗的判斷依據。緩存是提升性能的手段,允許短時間和數據庫的不一致

(5) 在互聯網架構中,很少追求強一致性,一般都是追求最終一致性


2 問題本質

如果非要保證緩存和數據庫的一致性,本質上是在解決分佈式一致性問題。

分佈式一致性問題解決方案有很多,可以選擇比如兩階段提交,TCC,本地消息表,MQ事務性消息等方案。

本回答摘自我的頭條文章《到底是先更新緩存,還是先更新數據庫》請您參考


敬請關注

請點擊關注按鈕【IT徐胖子】會持續為大家奉獻互聯網和技術乾貨內容,感謝支持


IT徐胖子


這個開發實際開發中還是常見的

方案一:添加過期時間,不過還是有延遲的,有校時間內還會讀取,無法一致性。

方案二:定時的Task把數據查詢結果,提前寫入緩存,但是即使這樣還是有延遲。

方案三:在任何數據操作,增刪改查前,清理緩存,同時重新緩存最新的副本。

最終方案:方案出二和方案三組合使用。

希望的回答有所幫助?


分享到:


相關文章: