溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis架構(gòu)中緩存雪崩的示例分析

發(fā)布時間:2022-02-14 10:03:48 來源:億速云 閱讀:155 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹Redis架構(gòu)中緩存雪崩的示例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

1 真實案例

云辦公系統(tǒng)用戶實時信息查詢功能優(yōu)化發(fā)布之后,系統(tǒng)發(fā)生宕機(jī)事件(系統(tǒng)掛起,頁面無法加載)。

1.1 背景

我們IM原有的一個功能,當(dāng)鼠標(biāo)移動到用戶頭像的時候,會顯示出用戶的基本信息。信息比較簡單,只包含簡單的用戶名、昵稱、性別、郵箱、電話等基本數(shù)據(jù),

這是一個典型的數(shù)據(jù)查詢,大概過程如下左側(cè),訪問用戶基本信息的時候會先去Redis中查一下,如果不存在,就把大約2W左右的用戶數(shù)據(jù)一次性取出來,保存在Redis中,因為用戶基本信息在同一張表上,用戶信息表的數(shù)據(jù)量也很少,所以一直也沒什么問題。

過程如下圖左側(cè)所示。

Redis架構(gòu)中緩存雪崩的示例分析

后續(xù)對功能做了優(yōu)化,原有采集的信息除了用戶的基本信息之外,還采集了教育經(jīng)歷、工作經(jīng)歷、所獲勛章等。

這些信息存儲在不同的表里面,所以采集過程是一個復(fù)雜的聯(lián)表查詢,特別是有些基礎(chǔ)表數(shù)據(jù)量比較大,執(zhí)行效率也是比較慢的。

如果把所有用戶全部取出來并存儲在一個Redis節(jié)點中,明顯已經(jīng)不適用,一個是批量查詢導(dǎo)致數(shù)據(jù)庫執(zhí)行效率慢,一個是Redis單節(jié)點數(shù)據(jù)太大。

所以開發(fā)同學(xué)做了下優(yōu)化,每次只取單個用戶的綜合信息存在Redis中,一個用戶建一個緩存,如上圖右側(cè)所示。 

1.2 問題處理

這種做法看著沒啥問題,當(dāng)晚發(fā)布后,在第二天的上午10點~11點就發(fā)生了系統(tǒng)瓶頸卡頓,最后掛起的情況,數(shù)據(jù)庫的內(nèi)存、CPU全部飆上去了。

第一時間的處理方法是降級,程序回滾到之前只提供基本信息的階段,其他的前端默認(rèn)顯示空信息。接著就是對問題進(jìn)行分析了,后確認(rèn)原因是產(chǎn)生了 緩存雪崩了。

新發(fā)布的系統(tǒng),緩存池是空的,在早上10點高峰期的時候,大量的人員到IM上進(jìn)行訪問,系統(tǒng)開始初次建立每個人的緩存信息,大量的請求查詢不到緩存,直接透過緩存池投向數(shù)據(jù)庫,造成瞬時DB請求量井噴。這是典型的緩存雪崩了。 

同時因為,失效時間相近(8小時失效),所以也有潛在的緩存雪崩。

應(yīng)急處理方案:適當(dāng)處理緩存的機(jī)制,采用布隆過濾器、空初始值、隨機(jī)緩存失效時間方式來預(yù)防緩存擊穿和緩存雪崩的產(chǎn)生。

最終解決方案:改回原來緩存全公司員工信息的方式,根據(jù)執(zhí)行計劃和SlowLog,優(yōu)化獲取員工信息的SQL腳本,去掉不需要的字段和無意義的連接。   

2 緩存雪崩

2.1 概念

緩存雪崩是指大量的key設(shè)置了相同的過期時間,導(dǎo)致在緩存在同一時刻全部失效,造成瞬時DB請求量大、壓力驟增,引起雪崩。

上面的哪個問題,初次訪問的數(shù)據(jù)都是未建立緩存的,跟同時失效的情況一樣,當(dāng)峰值期到來的時候,會大量的請求查詢不到緩存,直接透過緩存池投向數(shù)據(jù)庫,造成瞬時DB請求量井噴。

2.2 解決方案分析

2.2.1 緩存集群+數(shù)據(jù)庫集群

在系統(tǒng)容量設(shè)計的時候,應(yīng)該能夠預(yù)見后期會有大量的請求,所以在發(fā)生雪崩前對緩存集群實現(xiàn)高可用,如果是使用 Redis,可以使用 主從+哨兵 ,Redis Cluster 來避免 Redis 全盤崩潰的情況。

同樣的,也需要對數(shù)據(jù)庫進(jìn)行高可用保障,因為透過緩存之后,真正考驗的是數(shù)據(jù)庫的抗壓能力。所以 1主N從 甚至 數(shù)據(jù)庫集群 是我們需要重點去考慮的。

2.2.2 適當(dāng)?shù)南蘖?、降?/h5>

可以使用 Hystrix進(jìn)行限流 + 降級 ,比如像上面那種情況,一下子來了1W個請求,不是當(dāng)前系統(tǒng)的吞吐能力能夠承受的,假設(shè)單秒TPS的能力只能是 5000個,那么剩余的 5000 請求就可以走限流邏輯。

可以設(shè)置一些默認(rèn)值,然后調(diào)用我們自己降級邏輯去FallBack,保護(hù)最后的 MySQL 不會被大量的請求掛起。 除了Hystrix之外,阿里的Sentinel 和 Google的RateLimiter 都是不錯的選擇。

Sentinel 漏桶算法

Redis架構(gòu)中緩存雪崩的示例分析

RateLimiter 令牌桶算法

Redis架構(gòu)中緩存雪崩的示例分析

另外可以考慮使用用本地緩存來進(jìn)行緩沖,在 Redis Cluster 不可用的時候,不至于全線崩潰。

2.2.3 隨機(jī)過期時間

可以給緩存設(shè)置過期時間時加上一個隨機(jī)值時間,使得每個key的過期時間分布開來,不會集中在同一時刻失效。

隨機(jī)值我們團(tuán)隊的做法是:n * 3/4 + n * random() 。所以,比如你原本計劃對一個緩存建立的過期時間為8小時,那就是6小時 + 0~2小時的隨機(jī)值。

這樣保證了均勻分布在 6~8小時之間。如圖: 

Redis架構(gòu)中緩存雪崩的示例分析

2.2.4 緩存預(yù)熱

類似上面的那個案例,并不是還沒過期,而是新功能發(fā)布,壓根還沒建設(shè)過緩存,所以可以在峰值期之前先做好部分緩存,避免瞬時壓力太大。

所以如果10點是峰值期,那么可以預(yù)先在8~10點期間,可以逐漸的把大部分緩存建立起來。如圖:

Redis架構(gòu)中緩存雪崩的示例分析

3 緩存穿透

3.1 概念

緩存穿透是指訪問一個不存在的key,緩存不起作用,請求會穿透到DB,流量井噴時會導(dǎo)致DB掛掉。

比如 我們查詢用戶的信息,程序會根據(jù)用戶的編號去緩存中檢索,如果找不到,再到數(shù)據(jù)庫中搜索。如果你給了一個不存在的編號:XXXXXXXX,那么每次都比對不到,就透過緩存進(jìn)入數(shù)據(jù)庫。

這樣風(fēng)險很大,如果因為某些原因?qū)е麓罅坎淮嬖诘木幪柋徊樵?,甚至被惡意偽造編號進(jìn)行攻擊,那將是災(zāi)難。

3.2 解決方案分析

3.2.1 緩存空值

發(fā)生穿透的原因是緩存中沒有存儲這些空數(shù)據(jù)的key,或者壓根這個數(shù)據(jù)的key是不會存在的,從而導(dǎo)致每次查詢都進(jìn)入數(shù)據(jù)庫中。

我們就可以將這些key的值設(shè)置為null,并寫到緩存池中。后面再出現(xiàn)查詢這個key 的請求的時候,直接返回null,這樣就在緩存池中就被判斷返回了,壓力在緩存層中,不會轉(zhuǎn)移到數(shù)據(jù)庫上。

3.2.2 BloomFilter

我們稱作布隆過濾器,BloomFilter 類似于一個hbase set 用來判斷某個元素(key)是否存在于某個集合中。

這種方式在大數(shù)據(jù)場景應(yīng)用比較多,比如 Hbase 中使用它去判斷數(shù)據(jù)是否在磁盤上。還有在爬蟲場景判斷url 是否已經(jīng)被爬取過。

這種方案可以加在第一種方案中,在緩存之前在加一層 BloomFilter ,把存在的key記錄在BloomFilter中,在查詢的時候先去 BloomFilter 去查詢 key 是否存在,如果不存在就直接返回,存在再走查緩存 ,投入數(shù)據(jù)庫去查詢,這樣減輕了數(shù)據(jù)庫的壓力。

流程圖如下:

Redis架構(gòu)中緩存雪崩的示例分析

3.2.3 兩種方案的選擇判斷

前面說過,可能會存在一些惡意攻擊,偽造出大量不存在的key ,這種情況下如果我們?nèi)绻捎镁彺婵罩档霓k法,就會產(chǎn)生大量不存在key的null數(shù)據(jù)。顯然是不合適的,這時我們完全可以使用第二種方案進(jìn)行過濾掉這些key。

所以,判斷的依據(jù)是:

針對key非常多、請求重復(fù)率比較低的數(shù)據(jù),我們就沒有必要進(jìn)行緩存,使用 BloomFilter 直接過濾掉。

而對于空數(shù)據(jù)的key有限的,重復(fù)率比較高的,我們則可以采用 緩存空值的辦法 進(jìn)行處理。 

4 緩存擊穿

4.1 概念

一個存在的key,在緩存過期的一刻,同時有大量的請求,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增。(注意跟上面兩種的區(qū)別)

4.2 解決方案

4.2.1 鎖的方式

分布式鎖場景,在訪問key之前,采用SETNX(set if not exists)來設(shè)置另一個短期key來鎖住當(dāng)前key的訪問,訪問結(jié)束再刪除該短期key。

這種現(xiàn)象是多個線程同時去查詢數(shù)據(jù)庫的這條數(shù)據(jù),那么我們可以在第一個查詢數(shù)據(jù)的請求上使用一個 互斥鎖來鎖住它。

其他的線程走到這一步拿不到鎖就等著,等第一個線程查詢到了數(shù)據(jù),然后做緩存。后面的線程進(jìn)來發(fā)現(xiàn)已經(jīng)有緩存了,就直接走緩存。

鎖不好的地方就是在其他線程在拿不到鎖的時候就等待,這個會造成系統(tǒng)整體吞吐量降低,用戶體驗度也不好。

4.2.2 空初始值

這是一種短暫降級的方式:

如果一個緩存失效的時候,有無數(shù)個請求狂奔而來,而第一個請求從進(jìn)入緩存池,判空,再到數(shù)據(jù)庫檢索,再查詢出結(jié)果并返回設(shè)置緩存的這個過程里,緩存是不存在的。

這個就很危險,超高并發(fā)下這個短暫的過程足已讓千千萬萬請求投向數(shù)據(jù)庫。更別提這可能是個慢查詢,整個過程可能長達(dá)2s以上,那對數(shù)據(jù)庫是一種非常大的傷害。

業(yè)內(nèi)有一種做法叫做空初始值,短暫的局部降級來保證整個數(shù)據(jù)庫系統(tǒng)不被擊穿。大概流程如下:

Redis架構(gòu)中緩存雪崩的示例分析

可以看出,整個過程中我們犧牲了A、B、C、D的請求,他們拿回了一個空值或者默認(rèn)值,但是這局部的降級卻保證整個數(shù)據(jù)庫系統(tǒng)不被擁堵的請求擊穿。

以上是“Redis架構(gòu)中緩存雪崩的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI