對于緩存 + 數據庫讀寫,有個經典的Cache Aside Pattern:
讀取:先讀取緩存,緩存里沒有,讀取數據庫,然后返回響應,順便保存緩存:
更新:先更新數據庫,然后刪除緩存
為什么是刪除緩存而不是更新緩存?
- 并發情況下更新緩存可能會帶來更多問題,直接刪除緩存更加穩妥
- 緩存更新消耗更多資源,直接刪除,用時再從數據庫讀取,寫進緩存,更省性能
先更新數據庫,然后刪除緩存
一致性問題
假設更新數據庫成功,接下來還沒來得及刪除緩存,或者刪除緩存失敗,此時其他線程進來讀的就是臟數據。
既然刪除緩存失敗會導致臟數據,那么就得想辦法能讓它刪除成功。一般來說有兩種方法:消息隊列重試機制和監聽binlog異步刪除
消息隊列重試機制
如果刪除緩存失敗,向消息隊列發送消息,把刪除失敗的key放進去,消費消息隊列,獲取要刪除的key,然后重試刪除。
這樣做的話引入了消息隊列,對現有的業務造成了入侵,復雜度升高
監聽binlog異步刪除
用一個服務去監聽數據庫的binlog,獲取需要操作的數據。然后用另外一個服務獲取訂閱程序傳來的信息,進行緩存刪除操作。這樣對于業務的入侵就小了。
先刪除緩存,再更新數據庫
在并發情況下,先刪除緩存,再更新數據庫,此時數據庫還未更新成功,這時有其他線程進來,讀取緩存,緩存不存在,讀取數據庫,讀取的是就是舊值,此時緩存不一致就發生了。
解決方案便是延時雙刪:
就是在刪除緩存,更新數據庫之后,休眠一段時間后,再次刪除緩存。延時刪除之后,就把緩存里面緩存的舊值刪除了。
再有請求進來,就是讀取數據庫里的新值,再把新值保存進緩存。如果第二次刪除也失敗,那么就再次重試。
![]() | ![]() |