1. 程式人生 > >redis快取與資料庫一致性問題

redis快取與資料庫一致性問題

不一致產生的原因

我們在使用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的方式,非同步淘汰快取。 
這裡寫圖片描述

好處:業務程式碼侵入性低,將快取與資料庫不一致的時間儘可能縮小。