Redis緩存異常之緩存雪崩問題解讀
緩存異常:緩存雪崩、擊穿、穿透
當(dāng)發(fā)生緩存雪崩或擊穿時(shí),數(shù)據(jù)庫中還是保存了應(yīng)用要訪問的數(shù)據(jù)。
緩存擊穿,緩存更數(shù)據(jù)庫中都沒有應(yīng)用要訪問的數(shù)據(jù)。
1.緩存雪崩
1.1了解
緩存雪崩是指大量的應(yīng)用請(qǐng)求無法在 Redis 緩存中進(jìn)行處理,緊接著,應(yīng)用將大量請(qǐng)求發(fā)送到數(shù)據(jù)庫層,導(dǎo)致數(shù)據(jù)庫層的壓力激增。
1.2第一個(gè)原因是:緩存大量過期
緩存中有大量數(shù)據(jù)同時(shí)過期,導(dǎo)致大量請(qǐng)求無法得到處理。
具體說明:
當(dāng)數(shù)據(jù)保存在緩存中,并且設(shè)置了過期時(shí)間時(shí)--》某一個(gè)時(shí)刻,大量數(shù)據(jù)同時(shí)過期--》應(yīng)用再次訪問這些數(shù)據(jù),發(fā)生緩存缺失--》應(yīng)用把請(qǐng)求發(fā)送給數(shù)據(jù)庫,從數(shù)據(jù)庫中讀取數(shù)據(jù)--》應(yīng)用并發(fā)請(qǐng)求量大,數(shù)據(jù)庫壓力大,影響數(shù)據(jù)庫正常業(yè)務(wù)請(qǐng)求處理

第一種解決方式:微調(diào)緩存過期時(shí)間
如果業(yè)務(wù)層的確要求有些數(shù)據(jù)同時(shí)失效,在使用 EXPIRE 命令給每個(gè)數(shù)據(jù)設(shè)置過期時(shí)間時(shí),給這些數(shù)據(jù)的過期時(shí)間增加一個(gè)較小的隨機(jī)數(shù)(例如,隨機(jī)增加 1~3 分鐘)。
設(shè)置之后:不同數(shù)據(jù)的過期時(shí)間有所差別,但差別又不會(huì)太大,既避免了大量數(shù)據(jù)同時(shí)過期,同時(shí)也保證了這些數(shù)據(jù)基本在相近的時(shí)間失效,仍然能滿足業(yè)務(wù)需求
第二種解決方式:服務(wù)降級(jí)
- 當(dāng)業(yè)務(wù)應(yīng)用訪問的是非核心數(shù)據(jù)(例如電商商品屬性)時(shí),暫時(shí)停止從緩存中查詢這些數(shù)據(jù),而是直接返回預(yù)定義信息、空值或是錯(cuò)誤信息;
- 當(dāng)業(yè)務(wù)應(yīng)用訪問的是核心數(shù)據(jù)(例如電商商品庫存)時(shí),仍然允許查詢緩存,如果緩存缺失,也可以繼續(xù)通過數(shù)據(jù)庫讀取。
只有部分過期數(shù)據(jù)的請(qǐng)求會(huì)發(fā)送到數(shù)據(jù)庫,數(shù)據(jù)庫的壓力沒有那么大。
下面這張圖顯示的是服務(wù)降級(jí)時(shí)數(shù)據(jù)請(qǐng)求的執(zhí)行情況:
1.3第二個(gè)原因:Redis 緩存實(shí)例發(fā)生故障宕機(jī)
Redis 緩存實(shí)例發(fā)生故障宕機(jī)了,無法處理請(qǐng)求,這就會(huì)導(dǎo)致大量請(qǐng)求一下子積壓到數(shù)據(jù)庫層,從而發(fā)生緩存雪崩。
說明:
- 1個(gè) Redis 實(shí)例可以支持?jǐn)?shù)萬級(jí)別的請(qǐng)求處理吞吐量,而單個(gè)數(shù)據(jù)庫可能只能支持?jǐn)?shù)千級(jí)別的請(qǐng)求處理吞吐量,它們兩個(gè)的處理能力可能相差了近十倍。
- 由于緩存雪崩,Redis 緩存失效,所以,數(shù)據(jù)庫就可能要承受近十倍的請(qǐng)求壓力,從而因?yàn)閴毫^大而崩潰。
第一種解決方式:是在業(yè)務(wù)系統(tǒng)中實(shí)現(xiàn)服務(wù)熔斷或請(qǐng)求限流機(jī)制
服務(wù)熔斷
服務(wù)熔斷:是指在發(fā)生緩存雪崩時(shí),暫停業(yè)務(wù)應(yīng)用對(duì)緩存系統(tǒng)的接口訪問。為了防止引發(fā)連鎖的數(shù)據(jù)庫雪崩,甚至是整個(gè)系統(tǒng)的崩潰。
業(yè)務(wù)應(yīng)用調(diào)用緩存接口--》緩存客戶端不把請(qǐng)求發(fā)給 Redis 緩存實(shí)例--》Redis 緩存實(shí)例重新恢復(fù)服務(wù)后--》允許應(yīng)用請(qǐng)求發(fā)送到緩存系統(tǒng)。
避免了大量請(qǐng)求因緩存缺失,而積壓到數(shù)據(jù)庫系統(tǒng),保證了數(shù)據(jù)庫系統(tǒng)的正常運(yùn)行。
在業(yè)務(wù)系統(tǒng)運(yùn)行監(jiān)測(cè) Redis 緩存所在機(jī)器和數(shù)據(jù)庫所在機(jī)器的負(fù)載指標(biāo),例如每秒請(qǐng)求數(shù)、CPU 利用率、內(nèi)存利用率等。---》發(fā)現(xiàn) Redis 緩存實(shí)例宕機(jī)了,而數(shù)據(jù)庫所在機(jī)器的負(fù)載壓力突然增加(例如每秒請(qǐng)求數(shù)激增)--》緩存雪崩--》大量請(qǐng)求被發(fā)送到數(shù)據(jù)庫進(jìn)行處理--》啟動(dòng)服務(wù)熔斷機(jī)制,暫停業(yè)務(wù)應(yīng)用對(duì)緩存服務(wù)的訪問。
降低對(duì)數(shù)據(jù)庫的訪問壓力,如下所示:
服務(wù)熔斷雖然可以保證數(shù)據(jù)庫的正常運(yùn)行,但是暫停了整個(gè)緩存系統(tǒng)的訪問,對(duì)業(yè)務(wù)應(yīng)用的影響范圍大
請(qǐng)求限流機(jī)制
請(qǐng)求限流:在業(yè)務(wù)系統(tǒng)的請(qǐng)求入口前端控制每秒進(jìn)入系統(tǒng)的請(qǐng)求數(shù),避免過多的請(qǐng)求被發(fā)送到數(shù)據(jù)庫。
例子:
- 假設(shè)業(yè)務(wù)系統(tǒng)正常運(yùn)行時(shí),請(qǐng)求入口前端允許每秒進(jìn)入系統(tǒng)的請(qǐng)求是 1 萬個(gè),其中,9000 個(gè)請(qǐng)求都能在緩存系統(tǒng)中進(jìn)行處理,只有 1000 個(gè)請(qǐng)求會(huì)被應(yīng)用發(fā)送到數(shù)據(jù)庫進(jìn)行處理。
- 一旦發(fā)生了緩存雪崩,數(shù)據(jù)庫的每秒請(qǐng)求數(shù)突然增加到每秒 1 萬個(gè),可以啟動(dòng)請(qǐng)求限流機(jī)制,在請(qǐng)求入口前端只允許每秒進(jìn)入系統(tǒng)的請(qǐng)求數(shù)為 1000 個(gè),再多的請(qǐng)求就會(huì)在入口前端被直接拒絕服務(wù)。
- 使用了請(qǐng)求限流,就可以避免大量并發(fā)請(qǐng)求壓力傳遞到數(shù)據(jù)庫層。
- 服務(wù)熔斷或是請(qǐng)求限流機(jī)制。都是發(fā)生雪崩之后采取的措施,來應(yīng)對(duì) Redis 實(shí)例宕機(jī)導(dǎo)致的緩存雪崩問題。來降低雪崩對(duì)數(shù)據(jù)庫和整個(gè)業(yè)務(wù)系統(tǒng)的影響。
第二種解決方式:事前預(yù)防
通過主從節(jié)點(diǎn)的方式構(gòu)建 Redis 緩存高可靠集群。如果 Redis 緩存的主節(jié)點(diǎn)故障宕機(jī)了,從節(jié)點(diǎn)還可以切換成為主節(jié)點(diǎn),繼續(xù)提供緩存服務(wù),避免了由于緩存實(shí)例宕機(jī)而導(dǎo)致的緩存雪崩問題。
2.緩存擊穿
2.1了解
緩存擊穿是指,針對(duì)某個(gè)訪問非常頻繁的熱點(diǎn)數(shù)據(jù)的請(qǐng)求,無法在緩存中進(jìn)行處理,訪問該數(shù)據(jù)的大量請(qǐng)求,一下子都發(fā)送到了后端數(shù)據(jù)庫,導(dǎo)致了數(shù)據(jù)庫壓力激增,會(huì)影響數(shù)據(jù)庫處理其他請(qǐng)求。緩存擊穿的情況,經(jīng)常發(fā)生在熱點(diǎn)數(shù)據(jù)過期失效時(shí):

2.2解決方式
為了避免緩存擊穿給數(shù)據(jù)庫帶來的激增壓,對(duì)于訪問特別頻繁的熱點(diǎn)數(shù)據(jù),不設(shè)置過期時(shí)間。
對(duì)熱點(diǎn)數(shù)據(jù)的訪問請(qǐng)求,都可以在緩存中進(jìn)行處理,而 Redis 數(shù)萬級(jí)別的高吞吐量可以很好地應(yīng)對(duì)大量的并發(fā)請(qǐng)求訪問。
3.緩存穿透
3.1了解
緩存穿透是指要訪問的數(shù)據(jù)既不在 Redis 緩存中,也不在數(shù)據(jù)庫中,導(dǎo)致請(qǐng)求在訪問緩存時(shí),發(fā)生緩存缺失,再去訪問數(shù)據(jù)庫時(shí),發(fā)現(xiàn)數(shù)據(jù)庫中也沒有要訪問的數(shù)據(jù),應(yīng)用也無法從數(shù)據(jù)庫中讀取數(shù)據(jù)再寫入緩存,來服務(wù)后續(xù)請(qǐng)求。緩存也就成了“擺設(shè)”,如果應(yīng)用持續(xù)有大量請(qǐng)求訪問數(shù)據(jù),就會(huì)同時(shí)給緩存和數(shù)據(jù)庫帶來巨大壓力

3.2緩存穿透會(huì)發(fā)生兩種情況
- 業(yè)務(wù)層誤操作:緩存中的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)被誤刪除了,所以緩存和數(shù)據(jù)庫中都沒有數(shù)據(jù);
- 惡意攻擊:專門訪問數(shù)據(jù)庫中沒有的數(shù)據(jù)。
3.3第一種解決方式
緩存空值或缺省值
一旦發(fā)生緩存穿透,可以針對(duì)查詢的數(shù)據(jù),在 Redis 中緩存一個(gè)空值或是和業(yè)務(wù)層協(xié)商確定的缺省值(例如,庫存的缺省值可以設(shè)為 0)。
緊接著,應(yīng)用發(fā)送的后續(xù)請(qǐng)求再進(jìn)行查詢時(shí),就可以直接從 Redis 中讀取空值或缺省值,返回給業(yè)務(wù)應(yīng)用了,避免了把大量請(qǐng)求發(fā)送給數(shù)據(jù)庫處理,保持了數(shù)據(jù)庫的正常運(yùn)行。
3.4第二種解決方式
使用布隆過濾器快速判斷數(shù)據(jù)是否存在,避免從數(shù)據(jù)庫中查詢數(shù)據(jù)是否存在,減輕數(shù)據(jù)庫壓力。
布隆過濾器是如何工作?
布隆過濾器由一個(gè)初值都為 0 的 bit 數(shù)組和 N 個(gè)哈希函數(shù)組成,可以用來快速判斷某個(gè)數(shù)據(jù)是否存在,想標(biāo)記某個(gè)數(shù)據(jù)存在時(shí)(例如,數(shù)據(jù)已被寫入數(shù)據(jù)庫),布隆過濾器會(huì)通過三個(gè)操作完成標(biāo)記:
- 首先,使用 N 個(gè)哈希函數(shù),分別計(jì)算這個(gè)數(shù)據(jù)的哈希值,得到 N 個(gè)哈希值。
- 然后,把這 N 個(gè)哈希值對(duì) bit 數(shù)組的長度取模,得到每個(gè)哈希值在數(shù)組中的對(duì)應(yīng)位置
- 最后,把對(duì)應(yīng)位置的 bit 位設(shè)置為 1,這就完成了在布隆過濾器中標(biāo)記數(shù)據(jù)的操作。
如果數(shù)據(jù)不存在(例如,數(shù)據(jù)庫里沒有寫入數(shù)據(jù)),也就沒有用布隆過濾器標(biāo)記過數(shù)據(jù),那么,bit 數(shù)組對(duì)應(yīng) bit 位的值仍然為 0
布隆過濾器查詢
當(dāng)需要查詢某個(gè)數(shù)據(jù)時(shí),執(zhí)行計(jì)算過程,先得到這個(gè)數(shù)據(jù)在 bit 數(shù)組中對(duì)應(yīng)的 N 個(gè)位置。查看 bit 數(shù)組中這 N 個(gè)位置上的 bit 值。只要這 N 個(gè) bit 值有一個(gè)不為 1,這就表明布隆過濾器沒有對(duì)該數(shù)據(jù)做過標(biāo)記,所以,查詢的數(shù)據(jù)一定沒有在數(shù)據(jù)庫中保存。
圖中布隆過濾器是一個(gè)包含 10 個(gè) bit 位的數(shù)組,使用了 3 個(gè)哈希函數(shù),當(dāng)在布隆過濾器中標(biāo)記數(shù)據(jù) X 時(shí),X 會(huì)被計(jì)算 3 次哈希值,并對(duì) 10 取模,取模結(jié)果分別是 1、3、7。所以,bit 數(shù)組的第 1、3、7 位被設(shè)置為 1。當(dāng)應(yīng)用想要查詢 X 時(shí),只要查看數(shù)組的第 1、3、7 位是否為 1,只要有一個(gè)為 0,那么,X 就肯定不在數(shù)據(jù)庫中。
基于布隆過濾器的快速檢測(cè)特性,在把數(shù)據(jù)寫入數(shù)據(jù)庫時(shí),使用布隆過濾器做個(gè)標(biāo)記。當(dāng)緩存缺失后,應(yīng)用查詢數(shù)據(jù)庫時(shí),可以通過查詢布隆過濾器快速判斷數(shù)據(jù)是否存在。如果不存在,就不用再去數(shù)據(jù)庫中查詢了。防止緩存穿透。大量請(qǐng)求只會(huì)查詢 Redis 和布隆過濾器,而不會(huì)積壓到數(shù)據(jù)庫,也就不會(huì)影響數(shù)據(jù)庫的正常運(yùn)行。布隆過濾器可以使用 Redis 實(shí)現(xiàn),本身就能承擔(dān)較大的并發(fā)訪問壓力。
3.5第三種解決方式
在請(qǐng)求入口的前端進(jìn)行請(qǐng)求檢測(cè)
一個(gè)有效的應(yīng)對(duì)方案是在請(qǐng)求入口前端,對(duì)業(yè)務(wù)系統(tǒng)接收到的請(qǐng)求進(jìn)行合法性檢測(cè),把惡意的請(qǐng)求(例如請(qǐng)求參數(shù)不合理、請(qǐng)求參數(shù)是非法值、請(qǐng)求字段不存在)直接過濾掉,不讓它們?cè)L問后端緩存和數(shù)據(jù)庫。
這樣一來,也就不會(huì)出現(xiàn)緩存穿透問題了。
4.小結(jié):緩存三劍客:緩存雪崩、擊穿、穿透
4.1主要分析
- 緩存雪崩和擊穿主要是因?yàn)閿?shù)據(jù)不在緩存中了
- 緩存穿透則是因?yàn)閿?shù)據(jù)既不在緩存中,也不在數(shù)據(jù)庫中。
- 緩存雪崩或擊穿時(shí),一旦數(shù)據(jù)庫中的數(shù)據(jù)被再次寫入到緩存后,應(yīng)用又可以在緩存中快速訪問數(shù)據(jù)了,數(shù)據(jù)庫的壓力也會(huì)相應(yīng)地降低下來,
- 緩存穿透發(fā)生時(shí),Redis 緩存和數(shù)據(jù)庫會(huì)同時(shí)持續(xù)承受請(qǐng)求壓力。

4.4方案分析
服務(wù)熔斷、服務(wù)降級(jí)、請(qǐng)求限流這些方法都是屬于“有損”方案,在保證數(shù)據(jù)庫和整體系統(tǒng)穩(wěn)定的同時(shí),會(huì)對(duì)業(yè)務(wù)應(yīng)用帶來負(fù)面影響。例如使用服務(wù)降級(jí)時(shí),有部分?jǐn)?shù)據(jù)的請(qǐng)求就只能得到錯(cuò)誤返回信息,無法正常處理。如果使用了服務(wù)熔斷,那么,整個(gè)緩存系統(tǒng)的服務(wù)都被暫停了,影響的業(yè)務(wù)范圍更大。而使用了請(qǐng)求限流機(jī)制后,整個(gè)業(yè)務(wù)系統(tǒng)的吞吐率會(huì)降低,能并發(fā)處理的用戶請(qǐng)求會(huì)減少,會(huì)影響到用戶體驗(yàn)。
緩存雪崩和擊穿問題來說,服務(wù)熔斷、服務(wù)降級(jí)和請(qǐng)求限流這三種方法屬于有損方法,會(huì)降低業(yè)務(wù)吞吐量、拖慢系統(tǒng)響應(yīng)、降低用戶體驗(yàn)。不過,采用這些方法后,隨著數(shù)據(jù)慢慢地重新填充回 Redis,Redis 還是可以逐步恢復(fù)緩存層作用的。
4.3預(yù)防式方案
- 針對(duì)緩存雪崩,合理地設(shè)置數(shù)據(jù)過期時(shí)間,以及搭建高可靠緩存集群;
- 針對(duì)緩存擊穿,在緩存訪問非常頻繁的熱點(diǎn)數(shù)據(jù)時(shí),不要設(shè)置過期時(shí)間;
- 針對(duì)緩存穿透,提前在入口前端實(shí)現(xiàn)惡意請(qǐng)求檢測(cè),或者規(guī)范數(shù)據(jù)庫的數(shù)據(jù)刪除操作,避免誤刪除。
在講到緩存雪崩時(shí),我提到,可以采用服務(wù)熔斷、服務(wù)降級(jí)、請(qǐng)求限流的方法來應(yīng)對(duì)。這三個(gè)機(jī)制可以用來應(yīng)對(duì)緩存穿透問題嗎?
緩存穿透這個(gè)問題的本質(zhì)是查詢了 Redis 和數(shù)據(jù)庫中沒有的數(shù)據(jù),而服務(wù)熔斷、服務(wù)降級(jí)和請(qǐng)求限流的方法,本質(zhì)上是為了解決 Redis 實(shí)例沒有起到緩存層作用的問題,緩存雪崩和緩存擊穿都屬于這類問題。
在緩存穿透的場(chǎng)景下,業(yè)務(wù)應(yīng)用是要從 Redis 和數(shù)據(jù)庫中讀取不存在的數(shù)據(jù),此時(shí),如果沒有人工介入,Redis 是無法發(fā)揮緩存作用的。一個(gè)可行的辦法就是事前攔截,不讓這種查詢 Redis 和數(shù)據(jù)庫中都沒有的數(shù)據(jù)的請(qǐng)求發(fā)送到數(shù)據(jù)庫層。使用布隆過濾器也是一個(gè)方法,布隆過濾器在判別數(shù)據(jù)不存在時(shí),是不會(huì)誤判的,而且判斷速度非??欤坏┡袛鄶?shù)據(jù)不存在,就立即給客戶端返回結(jié)果。使用布隆過濾器的好處是既降低了對(duì) Redis 的查詢壓力,也避免了對(duì)數(shù)據(jù)庫的無效訪問。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Redis實(shí)現(xiàn)延遲任務(wù)的常見方案詳解
延遲任務(wù)(Delayed?Task)是指在未來的某個(gè)時(shí)間點(diǎn),執(zhí)行相應(yīng)的任務(wù),本文為大家整理了Redis實(shí)現(xiàn)延遲任務(wù)的幾個(gè)常見方案,希望對(duì)大家有所幫助2024-04-04
k8s部署redis cluster集群的實(shí)現(xiàn)
在Kubernetes中部署Redis集群面臨挑戰(zhàn),因?yàn)槊總€(gè)Redis實(shí)例都依賴于一個(gè)配置文件,該文件可以跟蹤其他集群實(shí)例及其角色。需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06
Redis動(dòng)態(tài)字符串SDS的實(shí)現(xiàn)
SDS在Redis中是實(shí)現(xiàn)字符串對(duì)象的工具,本文主要介紹了Redis動(dòng)態(tài)字符串SDS的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
Redis過期數(shù)據(jù)是否會(huì)被立馬刪除
這篇文章主要為大家介紹了Redis過期數(shù)據(jù)會(huì)被立馬刪除么的問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Redis?HyperLogLog數(shù)據(jù)統(tǒng)計(jì)輕量級(jí)解決方案詳解
這篇文章主要為大家介紹了Redis?HyperLogLog數(shù)據(jù)統(tǒng)計(jì)輕量級(jí)解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
實(shí)現(xiàn)在線?+?離線模式進(jìn)行遷移?Redis?數(shù)據(jù)實(shí)戰(zhàn)指南
這篇文章主要介紹了實(shí)現(xiàn)在線?+?離線模式進(jìn)行遷移?Redis?數(shù)據(jù)實(shí)戰(zhàn)指南的相關(guān)資料,需要的朋友可以參考下2023-01-01

