1. 程式人生 > >redis快取擊穿、穿透、熱點key、雪崩方案總結

redis快取擊穿、穿透、熱點key、雪崩方案總結

快取擊穿

首先,在將快取擊穿前,大家先來回憶下自己寫快取的方案,這裡我簡單畫了下流程圖:

image

當我們快取key設定過期時間,恰巧在這一刻這個key在某一刻被高併發的訪問,把所有的請求都打到了DB中這就可能會導致DB掛了。這個跟後面說的快取雪崩非常相似,這個和快取雪崩的區別在於這裡針對某一key快取,但是雪崩則指的是多個key,要解決方案有很多,比如讓一個執行緒構建快取,另外執行緒等待知道構建好,或者redis維護timeout欄位邏輯失效等等

解決方案:

1、使用同步方式鎖,單機用synchronized,lock可重入鎖等,分散式則用redis的setnx。

String get(String key) {  

   String value = redis.get(key);  

   if (value  == null) {  

    String brokenKey = "broken_"+key;  

    if (redis.setnx(brokenKey, "1")) {  

        redis.expire(brokenKey, 60)  //防止產生key不失效

        value = db.get(key);  

        redis.set(key, value);  

        redis.del(brokenKey);   

    } else { 

        //其他執行緒休息50毫秒後重試   

        Thread.sleep(50);   

        get(key);   

    }   

  }  

return value;

}  

優點: 保證一致性

缺點:程式碼複雜度增大,存在死鎖的風險

2、採用快取物理不過期,邏輯過期方式,在key對應的value設定一個timeout欄位。如果檢測到存的時間超過過期時間則非同步更新快取。

String get(final String key) {  

        T t = redis.get(key);  

        String value = t.getValue();  

        long timeout = t.getTimeout();  

        // 當邏輯的超時時間到了時,非同步構建快取  

        if (timeout <= System.currentTimeMillis()) {  

            threadPool.execute(new Runnable() {  

                public void run() {  

                    String brokenKey = "broken_"+key;  

                    if (redis.setnx(brokenKey, "1")) {  //redis加鎖

                        redis.expire(brokenKey, 60);  //防止產生key不失效

                        String dbValue = db.get(key);  

                        redis.set(key, dbValue);  

                        redis.del(brokenKey);  

                    }  

                }  

            });  

        }  

        return value;  

    }

優點: 不會阻塞執行緒 高效能

缺點:資料庫和快取可能會存在資料不一致

快取穿透

看了上面介紹的快取擊穿了,現在怎麼又出現了一個快取穿透呢,感覺字面上穿透和擊穿應該是一個意思啊,確實個人理解翻譯上是差不多,但是對於redis的專業技術語上卻是不同,快取穿透指的是一些惡意人攻擊,比如說登入的時候它請求一個一定不存在的使用者名稱時,那麼我們服務端正常應該是這樣處理,首先到快取中查詢,沒有在到資料查詢,看看好像是沒問題啊,查詢一下應該很快,如果是這一刻惡意攻擊發起幾百萬次請求,所以就會一直迴圈這個操作,一定不存在的使用者名稱會一直查詢資料庫,那麼你的資料庫和快取系統會不會掛呢?

解決方案

1、布隆過濾器處理,把key放到布隆過濾器中,獲取時檢視是否存在,如果存在則獲取快取、獲取資料庫(把key放入快取又2種方案,1、業務系統初始化 2、快取中獲取不到時單條插入到布隆過濾器)

優點: 高效能

缺點:布隆過濾器不支援刪除,需要單獨維護一個快取key的集合

2、查詢資料庫返回NULL時,不在是不放到redis中,而是redis中設定查詢結果為空的標示,比如:redis.set("key_none","NONE") 

優點: 簡單明瞭

缺點:需要為每個key再單獨維護一個標示空的key

熱點KEY

某個key訪問非常頻繁,當key在某個時刻失效的時候有大量執行緒來構建快取,導致負載增加,系統崩潰。

解決方案

熱點key也是快取失效打到資料庫,同快取擊穿一樣的解決方案

快取雪崩

快取雪崩上面又提到過和快取擊穿類似,雪崩指的是多個key同時失效,當高併發的請求過來所有請求都打到資料庫導致掛了

解決方案

1、儘量的把key的失效時間設定成不一樣,這樣可以減少併發帶來的壓力

2、同上快取擊穿方案