Redis緩存穿透、緩存擊穿和雪崩

一、緩存穿透

在討論緩存穿透之前,我們先來看下從緩存中讀取數據時的流程,如圖:

緩存穿透是什麼?

如果每次都去查一個“緩存和數據庫中都必不存在的數據(如id=-1的數據)”,因爲緩存中不存在,那麼每次請求都會打到DB上,從而導致緩存失去意義,在高併發的情況下就可能導致數據庫崩潰,這就是緩存穿透。

緩存穿透的解決方案

1、規範key過濾

    規範key的命名,並且統一緩存查詢的入口,在入口處對key的命名格式進行檢測,過濾掉不規範key的訪問,這樣可以過濾掉大部分的惡意攻擊。如約定項目中Redis緩存key的前綴都是以"公司名_項目名_REDIS_"開頭,不符合這個約定的key在一開始就過濾掉。

2、緩存空值

    簡單粗暴,如果查詢DB返回的數據爲空,我們仍然把這個空值放到Redis緩存中,只是將它的過期時間設置的很短,另外爲了避免不必要的內存消耗,可以定期清理空值的key。

3、加鎖

    根據key從緩存中獲取到的value爲空時,先鎖上,再去查DB將數據加載到緩存,若其它線程獲取鎖失敗,則等待一段時間後重試,從而避免了大量請求直接打到DB。單機可以使用synchronized或ReentrantLock加鎖,分佈式環境需要加分佈式鎖,如Redis分佈式鎖。

4、布隆過濾器

    我們想這樣一個問題,如果想判斷某個元素是不是在一個集合裏,一般做法是將集合中所有的元素保存起來,然後通過比較確定,比如HashMap。但是隨着集合中元素的增加,數據量超大時,我們需要的存儲空間也越來越大,甚至超過服務器內存,這時我們就不能再用HashMap等數據結構了。

    這時布隆過濾器就出場了,它的空間效率非常好,它是一個二進制向量,每一位存放的是0或1,初始時默認爲0,長下面這樣:

當一個元素加入集合時,通過 K 個 Hash 函數將這個元素映射成 k 個值 :K1、K2、K3...,把向量中下標爲K1、K2、K3...的位置置爲1 。

比如,元素X進來,將X作爲參數,通過3個hash函數的計算,分別得到3個值:Hash1(X)=5;Hash2(X)=2;Hash3(X)=9;那麼我們就將布隆過濾器中下標爲5、2、9的位置分別置爲1,如下,

可以看出,布隆過濾器根本沒有存放完整的數據,只是運用一系列隨機映射函數計算出位置,然後填充二進制向量,所以它的空間效率非常好。

※ 有一個元素Y,怎麼判斷Y在布隆過濾器中是否存在呢?

    同樣將Y作爲參數,通過3個hash函數的計算,分別得到3個值,比如是:4/6/8,我們只要看下標爲4/6/8的位置是不是都是1,

    > 如果都是1,則元素Y可能存在於集合中,爲什麼說可能呢——hash碰撞,不同的兩個元素,經過同樣的hash函數,計算出來的值,從概率上來講是有可能重複的。所以這也是布隆過濾器最大的缺點,存在誤判率。

    > 如果不全是1,則元素Y肯定不存在。

    即:當它說某個 key 不存在時,key一定不存在;當它說某個 key 存在時,key 可能存在。

※ 布隆過濾器是怎樣解決緩存穿透的?

    預先將所有緩存數據的key存放到布隆過濾器中,當一個查詢請求過來的時候,先判斷這個key在布隆過濾器中是否存在?

    > 如果不存在,直接返回提示,都不用去查緩存更不用說DB了;

    > 如果存在,則去查緩存,但我們知道布隆過濾器判斷存在有一定的誤判率,這裏我是這樣理解的,如果這個誤判率針對你們的業務場景是可被接受的則可以忽略,另外我們在用Guava實現布隆過濾器的時候可以指定誤判率不超過多少,你可以指定一個可被你接受的值。再或者,因爲布隆過濾器可以過濾掉絕大多數的惡意key,針對少部分的漏網之魚,我們可以在緩存層面使用功能上面說過的緩存空值或加鎖的方案。

二、緩存擊穿

緩存擊穿和緩存穿透不一樣!

說緩存擊穿之前,我們先來了解一個概念——熱點key,某個訪問非常頻繁,訪問量非常大的一個緩存key,我們叫做熱點key。

緩存擊穿是指某個熱點key在失效的瞬間(一般是緩存時間到期),持續的大併發請求穿破緩存,直接打到數據庫,就像在一個屏障上鑿開了一個洞,造成數據庫壓力瞬間增大,這就是緩存擊穿。

緩存擊穿的解決方案

    1、設置熱點key永不過期

    2、加鎖,根據熱點key從緩存中獲取到的value爲空時,先鎖上,再去查DB將數據加載到緩存,若其它線程獲取鎖失敗,則等待一段時間後重試,從而避免了大量請求直接打到DB。單機可以使用synchronized或ReentrantLock,分佈式需要加分佈式鎖,如Redis分佈式鎖。【爲了不阻塞對其他key的請求,此處可以用熱點key來加鎖】

三、緩存雪崩

緩存雪崩是指緩存由於某些原因整體或者大量失效,導致大量請求打到後端數據庫,從而導致數據庫崩潰,整個系統崩潰,發生災難。

導致緩存整體或大量失效的場景一般有:

    1、緩存服務宕機,如Redis集羣徹底崩潰;

    2、在某個集中的時間段內,系統預加載的緩存集中失效了;

預防和解決緩存雪崩

    1、保證緩存層服務高可用性,如使用Redis Sentinel 和 Redis Cluster,雙機房部署,保證Redis服務高可用。

    2、通過設置不同的過期時間,來錯開緩存過期,從而避免緩存集中失效。

 

感興趣的小夥伴可以關注一下博主的公衆號,1W+技術人的選擇,致力於原創技術乾貨,包含Redis、RabbitMQ、Kafka、SpringBoot、SpringCloud、ELK等熱門技術的學習&資料。

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