緩存穿透、緩存併發、緩存雪崩、緩存預熱

緩存穿透、緩存併發和緩存雪崩是常見的由高併發引起的緩存問題,而緩存預熱是緩存雪崩的一種解決方案。

這篇文章將來帶大家講解一下這四個概念。

1.緩存穿透

緩存穿透指的是併發使用大量緩存中不存在的key進行查詢,由於緩存無法命中,大量的查詢會穿過緩存直接查詢數據庫,使得數據庫壓力太大,導致數據庫可能被拖垮。

一般是受到了惡意的攻擊纔會導致這種問題,所以一旦遇到了緩存穿透的問題就會非常棘手,所以我們需要在代碼層面提前儘量規避掉這個問題。下面介紹幾種規避方案:

1.1 緩存空值

我們可以將從數據庫查詢不到結果的key也存到緩存中,value爲nul,這樣下次再查詢該key的時候就會在緩存中拿到null直接返回,而不會去數據庫查詢了。

如果攻擊者一直使用不同的key來攻擊,那麼不僅會引起緩存穿透的問題,同時還會導致緩存中存了大量null值的key(空值可以不用緩存太久,設置一個有效期可以提前釋放緩存),所以光緩存空值還不夠,還需要和其他方案相結合。

1.2 使用有規則的key

通常緩存的key都是一個指定前綴加一個id拼接組成的,那麼我們可以對這個進行合法校驗。

比如說id的長度是否合法,以及我們可以按照一定規則生成緩存key中的id,這樣在有查詢過來的時候我們可以先校驗一下key是否合法,不合法的話直接返回null。

1.3 使用布隆過濾器存儲所有可能的key,再進行校驗查詢的key

我們可以把所有可能的key放到一個集合裏,這樣一個請求過來的時候,可以先去集合裏看是否存在這個key,不存在就直接返回null。

那麼這個集合的數據類型怎麼定義呢?上一篇文章講一致性Hash算法的時候就分析了數組和紅黑樹的查詢效率,最終選擇了時間複雜度O(logN)的紅黑樹。

這裏我們校驗key是否存在在集合中的時候的要求是:
- 在集合中的元素一定要返回”在集合中”
- 但是不在集合中的元素我們允許一定概率的返回”在集合中”

所以我們可以使用布隆過濾器來幫我們實現這個數據結構,幫我們過濾掉大量不合法的key。

2.緩存併發

緩存併發指定是在查詢高併發的場景下,如果某一個緩存的key過期了,那麼將有大量的請求訪問數據庫並且會寫緩存,這樣會導致數據庫的壓力變大。

如果從緩存中查詢不到結果,那麼就會去數據庫查詢,那麼可以對這一段代碼加鎖,這樣其他請求就會進入等待,可以使用下面的代碼實現:

Object result = getFromCache(key);
if (result == null) {
    synchronized(object) {
        result = getFromCache(key);
        if (result != null) {
            return getFromCache;
        }
        getFromDB(key)
        ......
    }
}

當然了,加鎖會導致其他線程等待,那麼線程等待還是數據庫壓力二者之間就需要根據具體業務場景做一個抉擇了。

3.緩存雪崩

緩存雪崩指的是大量key同時失效或者緩存服務器重啓(清除緩存數據)之後,大量的用戶請求涌入服務器導致所有壓力全部落到數據庫上,從而壓垮數據庫。

緩存雪崩的問題的引發可以分爲兩個部分,一個是大量key同時過期,另一個是緩存服務器重啓,下面我們分兩中情況來分析一下解決方案。

3.1 大量key同時過期

大量key同時過期是由於我們寫入緩存的時候設置的有效時間相同導致的,所以我們可以在緩存時間上再加上一定的隨機數使得不同的key失效時間不一致就行了。

3.2 緩存服務器重啓

緩存服務器重啓導致緩存清空的問題可以使用緩存預熱來解決,我們直接看下面的緩存預熱。

4.緩存預熱

緩存預熱指的是在緩存系統重啓(沒有任何緩存數據)的時候,提前將熱點數據加載到緩存中。

我們可以在深夜或者用戶訪問量不大的時候進行緩存服務器的重啓,重啓之後進行緩存預熱,提前預熱部分熱點數據。

需要注意的是緩存預熱的時候可以對緩存數據的有效時間加上一定的隨機數,不然可能會導致大量的key同時過期。


通常在業務併發量不是太大的場景下很難遇到上面描述的幾個問題,但是理論知識我們還是要了解一下的。

今天關於緩存的分享就到這裏了,希望大家多多支持。


喜歡這篇文章的朋友,歡迎長按下圖關注公衆號lebronchen,第一時間收到更新內容。
掃碼關注

發佈了64 篇原創文章 · 獲贊 22 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章