一、緩存系統(tǒng)
1.1 緩存處理流程
前臺請求,后臺先從緩存中取數(shù)據(jù),取到直接返回結(jié)果,取不到時從數(shù)據(jù)庫中取,數(shù)據(jù)庫取到更新緩存,并返回結(jié)果,數(shù)據(jù)庫也沒取到,那直接返回空結(jié)果。

1.2 緩存系統(tǒng)需要解決的問題
設(shè)計一個緩存系統(tǒng),不得不要考慮的問題就是:緩存穿透、緩存擊穿與失效時的雪崩效應(yīng)。
二、緩存穿透
2.1 簡要描述
緩存穿透是指查找的數(shù)據(jù)在緩存和數(shù)據(jù)庫中都不存在,導(dǎo)致每一次請求數(shù)據(jù)從緩存中都獲取不到,而將請求打到數(shù)據(jù)庫服務(wù)器,但數(shù)據(jù)庫中也沒有對應(yīng)的數(shù)據(jù),最后每一次請求都到數(shù)據(jù)庫;如果在高并發(fā)場景或有人惡意攻擊,就會導(dǎo)致后臺數(shù)據(jù)庫服務(wù)器壓力增大,最終系統(tǒng)可能崩掉。如發(fā)起為id為“-1”的數(shù)據(jù)或id為特別大不存在的數(shù)據(jù)。這時的用戶很可能是攻擊者,攻擊會導(dǎo)致數(shù)據(jù)庫壓力過大。來個直接點的圖:

簡要說明:
緩存Redis服務(wù)器顏色說明:綠色塊代表有緩存數(shù)據(jù),粉色塊代表緩存中沒有數(shù)據(jù);綠色箭頭代表直接從緩存中獲取數(shù)據(jù);黃色箭頭代表穿過緩存從數(shù)據(jù)庫中查數(shù)據(jù),但不一定有。
流程大概如下:
1、大量客戶端發(fā)起大量請求到服務(wù)器;
2、服務(wù)器代碼邏輯將先經(jīng)過緩存,如果有緩存數(shù)據(jù)(綠色部分),直接從緩存中獲取數(shù)據(jù)數(shù)據(jù)返回;如果緩存中沒有數(shù)據(jù)(粉色部分),請求就會直接打到數(shù)據(jù)庫服務(wù)器(如黃色箭頭)。
3、如果存在大量無緩存數(shù)據(jù)的請求,最終數(shù)據(jù)庫將因為過大壓力而崩掉,導(dǎo)致系統(tǒng)不可用。
2.2 常用解決措施
緩存空值:如果沒有在數(shù)據(jù)庫中獲取到數(shù)據(jù),可以將其對應(yīng)鍵的空值進(jìn)行緩存,并設(shè)置較短過期時間;優(yōu)點:在過期時間內(nèi)直接通過緩存返回空值;從而避免數(shù)據(jù)庫壓力;缺點:消耗Redis內(nèi)存:如果是攻擊者換著非常規(guī)的鍵值請求,如果每次都緩存到Redis中,大量的空數(shù)據(jù)也占內(nèi)存空間;數(shù)據(jù)不一致:如果是正常數(shù)據(jù),剛開始沒有數(shù)據(jù),然后將空值進(jìn)行緩存,并設(shè)置短暫的過期時間;如果在過期時間內(nèi)正常維護(hù)了對應(yīng)的數(shù)據(jù),此時取到值仍是空,并沒有去數(shù)據(jù)庫中獲取新維護(hù)數(shù)據(jù),導(dǎo)致數(shù)據(jù)獲取不一致。
布隆過濾器:加一層過濾器進(jìn)行攔截,判斷請求對應(yīng)的鍵是否在過濾器中,如果不在就直接返回,不去請求數(shù)據(jù)庫,也不用緩存空值。而布隆過濾器采用bit位的形式標(biāo)識對應(yīng)鍵(每個鍵進(jìn)行Hash過后都會得到具體的位置)是否存在,可以用極少的空間標(biāo)識超大量的數(shù)據(jù)。缺點:布隆過濾器可以判斷數(shù)據(jù)一定不在過濾器中,而對于存在的判斷有誤判率,因為Hash算法存在沖突的情況。
操作:
1、項目啟動時將member表里的id一次性加載出來,循環(huán)通過布隆過濾器的bf.add方法添加元素,如果有新member創(chuàng)建,則也調(diào)用bf.add添加元素。
2、前端請求數(shù)據(jù),先調(diào)用布隆過濾器的bf.exists方法判斷是否存在,如果存在則調(diào)用Redis緩存,如果緩存存在則直接返回,如果不存在再查數(shù)據(jù)庫。
三、緩存雪崩
3.1 簡要描述
緩存雪崩是指突然緩存層不可用,導(dǎo)致大量請求直接打到數(shù)據(jù)庫,最終由于數(shù)據(jù)庫壓力過大可能導(dǎo)致系統(tǒng)崩掉。緩存層不可用指以下兩方面:
- 緩存服務(wù)器宕機,系統(tǒng)將請求打到數(shù)據(jù)庫;
- 緩存數(shù)據(jù)突然大范圍集中過期失效,導(dǎo)致大量請求打到數(shù)據(jù)庫重新加載數(shù)據(jù);

簡要說明:
緩存Redis服務(wù)器顏色說明:綠色塊代表有緩存數(shù)據(jù),粉色塊代表緩存中沒有數(shù)據(jù);白色塊代表大范圍失效的緩存數(shù)據(jù),綠色箭頭代表直接從緩存中獲取數(shù)據(jù);黃色箭頭代表穿過緩存從數(shù)據(jù)庫中查數(shù)據(jù)。
流程大概如下:
1、大量客戶端發(fā)起大量請求到服務(wù)器;
2、服務(wù)器代碼邏輯將先經(jīng)過緩存,如果有緩存數(shù)據(jù)(綠色部分),直接從緩存中獲取數(shù)據(jù)數(shù)據(jù)返回;如果緩存過期(白色塊部分),請求就會直接打到數(shù)據(jù)庫服務(wù)器(如黃色箭頭)。
3、如果存在大量熱數(shù)據(jù)的請求,但熱數(shù)據(jù)又大范圍過期,最終數(shù)據(jù)庫將因為過大壓力崩掉,導(dǎo)致系統(tǒng)不可用。
3.2 常用解決措施
緩存預(yù)熱:在高峰期還沒到來時,提前將熱數(shù)據(jù)加載到緩存中,避免高峰期來臨時數(shù)據(jù)庫壓力過大。
均勻設(shè)置過期時間:針對不同的熱點數(shù)據(jù),將過期時間加上一個隨機值,讓過期時間不集中在一個點,從而減小很大部分?jǐn)?shù)據(jù)庫壓力;
多級緩存:除了使用Redis緩存,還可以根據(jù)業(yè)務(wù)增加一些熱點數(shù)據(jù)的其他緩存,比如內(nèi)存緩存,可以將各級的緩存有效期分開,這種方式也能緩解數(shù)據(jù)庫的壓力;
限流、降級:如果壓力過大,避免把系統(tǒng)搞崩,可以增加一些限流手段,不管是中間件還是消息隊列等,主要保證系統(tǒng)的可用。
加互斥鎖:目的就是加鎖獨占操作,讓一個操作向緩存中重新加載數(shù)據(jù),讓請求操作等待,其實這樣的體驗不好,慎用。如果要用,要超級注意鎖的性能和穩(wěn)定性。
對于緩存層整體崩掉的情況:使用高可用架構(gòu),比如之前說到的主從復(fù)制、哨兵、集群,根據(jù)需求進(jìn)行對應(yīng)架構(gòu),保證緩存層不崩掉。
四、緩存擊穿
4.1 簡要描述
緩存擊穿是指在超級熱點數(shù)據(jù)突然過期,導(dǎo)致針對超級熱點的數(shù)據(jù)請求在過期期間直接打到數(shù)據(jù)庫,這樣數(shù)據(jù)庫服務(wù)器會因為某一超熱數(shù)據(jù)導(dǎo)致壓力過大而崩掉。超熱數(shù)據(jù):比如秒殺時的數(shù)據(jù),某寶、某東、某多多這種平臺的數(shù)據(jù)如果在秒殺時間段失效,請求量足矣讓數(shù)據(jù)庫崩掉。
和緩存雪崩不同的是, 緩存擊穿指并發(fā)查同一條數(shù)據(jù),緩存雪崩是不同數(shù)據(jù)都過期了,很多數(shù)據(jù)都查不到從而查數(shù)據(jù)庫。
對緩存擊穿的另外一種描述:
對于一些設(shè)置了過期時間的key,如果這些key可能會在某些時間點被超高并發(fā)地訪問,是一種非?!盁狳c”的數(shù)據(jù)。這個時候,需要考慮一個問題:緩存被“擊穿”的問題,這個和緩存雪崩的區(qū)別在于這里針對某一key緩存,前者則是很多key。
緩存在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期一般都會從后端DB加載數(shù)據(jù)并回設(shè)到緩存,這個時候大并發(fā)的請求可能會瞬間把后端DB壓垮。

簡要說明:
緩存Redis服務(wù)器顏色說明:綠色塊代表有緩存數(shù)據(jù),粉色塊代表緩存中沒有數(shù)據(jù);白色圈代表超級熱點緩存數(shù)據(jù)過期失效,綠色箭頭代表直接從緩存中獲取數(shù)據(jù);黃色箭頭代表穿過緩存從數(shù)據(jù)庫中查數(shù)據(jù)。
流程大概如下:
1、大量客戶端發(fā)起大量請求到服務(wù)器;
2、服務(wù)器代碼邏輯將先經(jīng)過緩存,如果有緩存數(shù)據(jù)(綠色部分),直接從緩存中獲取數(shù)據(jù)數(shù)據(jù)返回;如果超熱緩存數(shù)據(jù)過期(白色圈部分),請求就會直接打到數(shù)據(jù)庫服務(wù)器(如黃色箭頭)。
3、超級熱點數(shù)據(jù)過期失效,如秒殺數(shù)據(jù),如果在秒殺時段失效,最終數(shù)據(jù)庫將因為過大壓力崩掉,導(dǎo)致系統(tǒng)不可用。
注:這個只是針對超熱點數(shù)據(jù),而不是大范圍數(shù)據(jù)。
4.2 常用解決措施
熱點數(shù)據(jù)不過期:像這種超熱數(shù)據(jù)就設(shè)置永不過期。避免過期失效讓數(shù)據(jù)庫壓力過大而崩。
加二級緩存
加互斥鎖:目的就是加鎖,然后向緩存中重新加載數(shù)據(jù),讓請求等待,其實這樣的體驗不好,慎用。如果要用,要超級注意鎖的性能和穩(wěn)定性。
資料來源
Redis緩存穿透、緩存雪崩、緩存擊穿好好說說
緩存穿透、緩存擊穿、緩存雪崩區(qū)別和解決方案
緩存穿透,緩存擊穿,緩存雪崩解決方案分析