關於緩存的各種問題及其解決方法

在高qps的系統中,爲了避免每次請求都查詢數據庫,給數據庫造成很大的壓力,一般都會使用緩存來減輕數據庫的訪問壓力。不過緩存的一些問題會導致緩存失去應有的作用,使得請求還是訪問了數據庫,給數據庫造成了很大的壓力。這些問題包括

  • 緩衝穿透
  • 緩衝擊穿
  • 緩衝雪崩

 

一、緩存穿透

緩衝穿透是指請求查詢的數據,在數據庫中根本不存在,所以緩存中也不會有,這樣每次請求都會查詢數據庫的現象。

常見的解決方案有兩種,一是緩存空值,二是使用布隆過濾器。

1.1 緩存空值

發生緩存穿透,是因爲緩存中沒有存儲這些空數據的key。從而導致每次查詢都到數據庫去了。那麼我們就可以爲這些key在緩存中設置空值null。後面再出現查詢這個key 的請求的時候,直接返回設置的空值即可。當然爲了健壯性,我們要對這些key設置過期時間,以防止真的出現數據。

1.2 bloom filter(布隆過濾器)

BloomFilter 類似於一個hbase set 用來判斷某個元素(key)是否存在於某個集合中。我們把有數據的key都放到BloomFilter中,每次查詢的時候都先去BloomFilter判斷,如果沒有就直接返回null,這樣也就不會查詢數據庫了。

在實際應用中,這兩種方法會組合使用,即先經過布隆過濾器,過濾掉不存在的key,然後查詢緩存,如果沒有數據,那麼返回緩存中的空值。另外,BloomFilter沒有刪除操作,對於刪除的key,查詢就會經過BloomFilter然後查詢緩存再查詢數據庫,所以對於刪除的key,也可以在緩存中存儲空值來避免查詢數據庫。

另外,還可以在接口層做一些基礎校驗,避免惡意攻擊,比如用戶鑑權,id<=0的直接攔截等。

二、緩存擊穿

緩存擊穿是指大量請求同時查詢一個key時,這個key剛好失效,使得大量請求都去查詢數據庫的現象。這樣的key一般是熱點數據。緩存擊穿是單個key的過期問題引起的,而緩存雪崩是大量key過期引起的。

解決方案有兩種,一是使用互斥鎖或分佈式鎖,二是設置熱點數據永不過期

2.1 互斥鎖或分佈式鎖

當這些請求查詢數據庫時,只有第一個請求會拿到鎖,其他請求的線程因爲沒有鎖而被阻塞,就不會查詢數據庫。等第一個請求查詢後,數據進行緩存,其他請求發現緩存中已經有數據,就不會再查詢數據庫。

//互斥鎖
public static String getDate(String key) throws InterruptedException{
    String result = getDataFromCache(key);
    if(result == null){
        if(reenLock.tryLock()){
            result = getDataFromDb(key);
            if(result != null){
                setDataToCache(key, result);
            }
            reenLock.unlock();
        }else{
            Thread.sleep(100);
            result = getData(key);
        }
    }
    return result;
}

三、緩存雪崩

緩存雪崩是指由於某些原因,在同一時刻發生大量緩存失效,導致大量請求查詢數據庫的現象。比如緩存服務器宕機或大量緩存過期時間相同而同時過期等原因。

解決方案

3.1 使用緩存集羣

對於緩存服務器宕機的情況,可以使用緩存集羣實現高可用性。使用集羣的另一個好處是可以把不同熱點數據分佈在不同緩存服務器上。

3.2 設置分散的過期時間

對於大量緩存同時過期的問題,可以將過期時間分散開,比如在原有過期時間上增加一個隨機值,這樣緩存的過期時間的重複率就會降低,就很難引發集體失效的情況。

3.3 設置熱點數據永不過期

 

更多參考

https://blog.csdn.net/zeb_perfect/article/details/54135506

https://juejin.im/post/5c9a67ac6fb9a070cb24bf34

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章