問題:當數據庫有數據更新時,怎樣保證redis緩存中的數據與數據庫數據一致?
Redis更新的正確方法
看到好些人在寫更新緩存數據代碼時,先刪除緩存,然后再更新數據庫,而后續的操作會把數據再裝載的緩存中。然而,這個是邏輯是錯誤的。試想,兩個並發操作,一個是更新操作,另一個是查詢操作,更新操作刪除緩存后,查詢操作沒有命中緩存,先把老數據讀出來后放到緩存中,然后更新操作更新了數據庫。於是,在緩存中的數據還是老的數據,導致緩存中的數據是臟的,而且還一直這樣臟下去了。
視圖分析:
寫流程(更新策略)
1.先淘汰 cache(刪除緩存);
2.再寫 DB(更新數據庫)。
讀流程
1.先讀 cache,如果數據命中 hit 則返回;
2.如果數據未命中 miss 則讀 DB;
3.將 DB 中讀取出來的數據入緩存。
什么情況下可能出現緩存和數據庫不一致呢?
在分布式環境下,數據的讀寫都是並發的,上游有多個應用,通過一個服務的多個部署(為了保證可用性,一定是部署多份的),對同一個數據進行讀寫,在數據庫層面並發的讀寫並不能保證完成順序,也就是說后發出的讀請求很可能先完成(讀出臟數據):
1.發生了寫請求 A,A 的第一步淘汰了 cache(如上圖中的1);
2.A 的第二步寫數據庫,發出修改請求(如上圖中的2);
3.發生了讀請求 B,B 的第一步讀取 cache,發現 cache 中是空的(如上圖中的步驟3);
4.B 的第二步讀取數據庫,發出讀取請求,此時 A 的第二步寫數據還沒開始,讀出了一個臟數據放入 cache(如上圖中的步驟4)。
最終導致緩存與數據庫不一致。
總結
先更新數據庫,后刪除緩存(建議,使用場景最多),但是,是不是這個就不會有並發問題了?不是的,比如,一個是讀操作,但是沒有命中緩存,然后就到數據庫中取數據,此時來了一個寫操作,寫完數據庫后,讓緩存失效,然后,之前的那個讀操作再把老的數據放進去,所以,會造成臟數據。