緩存和數據庫一致性問題,有很多解決方案,沒有最完美的方案,只有適合自身業務的盡可能完美的方案。
緩存由於其高並發和高性能的特征,已經在項目中被廣泛應用。
查詢時一般先查詢緩存,如果緩存命中的話,那么直接將數據返回。
如果緩存中沒有數據(如失效,或者根本沒設置數據),那么,應用程序先從數據庫中查詢數據,如果不為空,則將數據放在緩存中。
那么更新時,怎么處理緩存和數據庫呢?先更新數據庫后更新緩存?先更新數據庫后更新緩存?或者先淘汰緩存后更新數據庫?
為什么沒有先更新緩存后更新數據庫?
1):如果更新數據庫失敗,那么就造成了數據不一致
先更新數據庫后更新緩存的問題:
1):兩個線程並發更新數據庫再更新緩存可能出現緩存更新順序問題
2):如果更新頻繁,讀少的情況,那么緩存也被頻繁更新,造成不必要的開銷
3):如果緩存的值是需要經過一系列復雜計算的,那么每次都去更新緩存無疑是浪費性能的
先刪緩存后更新數據庫的問題:
1):線程A刪除緩存更新完數據庫前,線程B沒有命中緩存,從數據庫中查詢到了更新前的值存入緩存中
解決方案:延時雙刪策略(推薦使用)
即先刪除緩存,再更新數據庫,休眠一段時間,再刪除緩存
偽代碼如下:
public void write(String key,Object data){ redis.delKey(key); db.updateData(data); Thread.sleep(1000); redis.delKey(key); }
為什要休眠1秒鍾?為了將這1秒內造成的臟數據刪除,可能有線程讀取到了更新前的舊數據還未來得及寫入緩存
休眠的時間多少如何確定?評估自身項目讀數據業務邏輯的耗時,在這基礎了加100ms即可。可以確保臟數據已經寫入緩存中
讀寫分離怎么辦?
也是采用延時雙刪策略,休眠時間確保完成主從同步
為了避免休眠造成吞吐量降低,可以將第二次刪除作為異步操作
第二次刪除失敗怎么辦?
刪除在更新期間寫入緩存的舊值失敗
解決方案:將需要刪除的key發送到消息隊列,然后自己消費消息,獲得需要刪除的key,繼續重試刪除操作,直到成功。
先更新數據庫后刪緩存
即Cache Aside Pattern,即緩存旁路模式
失效:應用程序先從緩存中取數據,沒有取到,則從數據庫中取數據,成功后,放到緩存中
命中:應用程序從緩存中渠道數據,然后返回
更新:先把數據存到數據庫中,成功后,再刪除緩存