不一致產生的原因
我們在使用redis過程中,通常會這樣做:先讀取緩存,如果緩存不存在,則讀取數據庫。偽代碼如下:
Object stuObj = new Object(); public Stu getStuFromCache(String key){ Stu stu = (Stu) redis.get(key); if(stu == null){ //加鎖的目的是防止過多的查詢走到數據庫層 synchronized (stuObj) { stu = (Stu) redis.get(key); if(stu == null){ Stu stuDb = db.query(); redis.set(key, stuDb); } } } return stu; }
寫數據庫的偽代碼如下:
public void setStu(){ redis.del(key); db.write(obj); }
不管是先寫庫,再刪除緩存;還是先刪緩存,再寫庫,都有可能出現數據不一致的情況
因為寫和讀是並發的,沒法保證順序,如果刪了緩存,還沒有來得及寫庫,另一個線程就來讀取,發現緩存為空,則去數據庫中讀取數據寫入緩存,此時緩存中為臟數據。如果先寫了庫,再刪除緩存前,寫庫的線程宕機了,沒有刪除掉緩存,則也會出現數據不一致情況。 如果是redis集群,或者主從模式,寫主讀從,由於redis復制存在一定的時間延遲,也有可能導致數據不一致。
優化思路
雙刪加超時
在寫庫前后都進行redis.del(key)
操作,並且設定合理的超時時間。這樣最差的情況是在超時時間內存在不一致,當然這種情況極其少見,可能的原因就是服務宕機。此種情況可以滿足絕大多數需求。 當然這種策略要考慮redis和數據庫主從同步的耗時,所以在第二次刪除前最好休眠一定時間,比如500毫秒,這樣毫無疑問又增加了寫請求的耗時
異步淘汰緩存
通過讀取binlog的方式,異步淘汰緩存。
好處:業務代碼侵入性低,將緩存與數據庫不一致的時間盡可能縮小。
摘自:https://www.cnblogs.com/senlinyang/p/8830611.html