溫馨提示×

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

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

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

發(fā)布時(shí)間:2021-07-02 11:42:28 來源:億速云 閱讀:176 作者:小新 欄目:關(guān)系型數(shù)據(jù)庫

這篇文章主要為大家展示了“Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析”這篇文章吧。

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

  • 緩存雪崩

  • 緩存擊穿

  • 緩存穿透

相信這三個(gè)問題,網(wǎng)上已經(jīng)有很多的伙伴講過了,但是今天我還是想說下,會(huì)多畫圖,讓大家加深印象,這三個(gè)問題也高頻的面試題,但是能把這幾個(gè)問題說清楚,也是需要技巧的。

再說這三個(gè)問題的時(shí)候,先說下正常的請(qǐng)求流程,看圖說話:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

上圖的意思大致如下:

首先會(huì)在你的代碼中,可能是tomcat 也可以是你的rpc 服務(wù)中,先判斷緩存cache 中是否存在你想要的數(shù)據(jù),如果存儲(chǔ)了,那么直接返回給調(diào)用端,如果不存在,那么就需要查詢數(shù)據(jù)庫,查詢出結(jié)果來,再繼續(xù)緩存到cache中,然后返回結(jié)果給調(diào)用方,下次再來的查詢的時(shí)候,也就命中緩存了。

緩存雪崩

定義

記得之前在做推薦系統(tǒng)的時(shí)候,有些數(shù)據(jù)是離線算法算出來的,需求是看了這個(gè)商品會(huì)推薦哪些相似的商品,這個(gè)算出來之后會(huì)存儲(chǔ)到hbase,同時(shí)存儲(chǔ)到redis,由于都是批量算法出來的,再存儲(chǔ)到redis 的時(shí)候,如果過期時(shí)間設(shè)置相同,那么就會(huì)造成大批量的key ,在同一時(shí)刻失效,那么就會(huì)有大批量的請(qǐng)求會(huì)被打到后臺(tái)的數(shù)據(jù)庫上,因?yàn)閿?shù)據(jù)庫的吞吐量是有限的,很有可能會(huì)把數(shù)據(jù)庫打垮的,這種情況就是緩存雪崩,看圖說話:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

這個(gè)主要是說明一個(gè)緩存雪崩出現(xiàn)的場(chǎng)景,尤其是定時(shí)任務(wù)在批量設(shè)置cache的時(shí)候,一定要注意過期時(shí)間的設(shè)置。

如何預(yù)防雪崩

其實(shí)也很簡單,就是在你批量設(shè)置cache的緩存時(shí)間的時(shí)候,給設(shè)置的緩存時(shí)間,設(shè)置一個(gè)隨機(jī)數(shù)(如隨機(jī)數(shù)可以10分鐘內(nèi)的數(shù)字,隨機(jī)數(shù)的生成可以用java的Random生成),這樣,就不會(huì)出現(xiàn)大量的key,再同一時(shí)刻集體失效了,看圖說話:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

如果真的發(fā)生了雪崩怎么辦?

流量不是很大,數(shù)據(jù)庫能抗住,ok,恭喜你逃過一劫。

流量很大,超過了數(shù)據(jù)庫所能處理的請(qǐng)求數(shù)的極限,數(shù)據(jù)庫down機(jī)了,也恭喜你領(lǐng)了一個(gè)P0事故單。

流量很大,如果你的數(shù)據(jù)庫有限流方案,當(dāng)達(dá)到了限流設(shè)置的參數(shù),那么就會(huì)拒絕請(qǐng)求,從而保護(hù)了后臺(tái)db。這里對(duì)限流多說幾句。

可以通過設(shè)置每秒請(qǐng)求數(shù),來限制大量的請(qǐng)求到達(dá)db端,注意這里的每秒請(qǐng)求數(shù),或者說是并發(fā)數(shù),并不是數(shù)據(jù)當(dāng)前的每秒請(qǐng)求數(shù),可以設(shè)置為查詢某個(gè)key 對(duì)應(yīng)的每秒請(qǐng)求數(shù)量,這樣做的目的,是防止大量相同key的請(qǐng)求到達(dá)后端數(shù)據(jù)庫,這樣就能攔截了大部分請(qǐng)求了。

看圖說話:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

這樣相同的key,就會(huì)被限流了大部分請(qǐng)求,從而保護(hù)了數(shù)據(jù)庫db。

其實(shí)限流還分為本地限流和分布式限流兩種,后面的文章里,我會(huì) 介紹本地限流和redis 實(shí)現(xiàn)的分布式限流。

緩存擊穿

定義

比如在某網(wǎng)站在進(jìn)行雙十一或者在搞秒殺等運(yùn)營活動(dòng)的時(shí)候,那么此時(shí)網(wǎng)站流量一般都會(huì)很大的,某個(gè)一個(gè)商品因?yàn)榇黉N會(huì)成為爆品,流量超級(jí)的大,如果這個(gè)商品,在這個(gè)時(shí)候,由于某種原因,在cache內(nèi)失效了,那么就瞬間這個(gè)key的流量都會(huì)涌向數(shù)據(jù)庫了,那么db最終挺不住了,down了,后果可想而知啊,正常其他的數(shù)據(jù)也查詢不了。

看圖說話:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

redis 中的huawei pro 這個(gè)key 突然失效了,可能是到期了,可能是內(nèi)存不夠被淘汰了,那么就會(huì)有大流量的請(qǐng)求到達(dá)redis ,發(fā)現(xiàn)redis 沒有這個(gè)key,那么這些流量,就會(huì)轉(zhuǎn)到DB 上去,查詢對(duì)應(yīng)的huawei pro,此時(shí)DB 挺不住了,down了。

如何解決

其實(shí)歸根到底還是不能讓更多的流量到達(dá)DB就行了,所以我們就是要限制到達(dá)db的流量就可以了。

1、限流

和上面說的類似,主要是限制某個(gè)key的流量,當(dāng)這個(gè)key ,被擊穿后,限制只有一個(gè)流量進(jìn)入到db,其他都被拒絕,或者等待重試查詢r(jià)edis。

限流的圖可以參考緩存擊穿限流的圖。

這里也會(huì)分本地限流和分布式限流 。

何為本地限流,就是在本地單個(gè)實(shí)例范圍內(nèi),限制這個(gè)key的流量多少,只對(duì)當(dāng)前實(shí)例有效。
何為分布式限流呢,就是在分布式的環(huán)境下,多個(gè)實(shí)例的范圍內(nèi),這個(gè)key的限制流量的累加是來自多個(gè)實(shí)例的流量,達(dá)到限制,所有的實(shí)例都會(huì)限制流量到達(dá)DB。

2、利用分布式鎖

這里簡單說下分布式鎖的定義,在并發(fā)場(chǎng)景下,需要使用鎖對(duì)共享資源互斥訪問來保證線程安全;同樣,在分布式場(chǎng)景下,也需要一種機(jī)制來保證對(duì)多節(jié)點(diǎn)共享資源的互斥訪問,實(shí)現(xiàn)機(jī)制就是分布式鎖。

在這里共享資源就是例子中的huawei pro,也就是在訪問db中的huawei pro 的時(shí)候,要保證只有一個(gè)線程或者一個(gè)流量去訪問,就達(dá)到了分布式鎖的效果。

看圖說話:

去搶鎖:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

大量請(qǐng)求在沒有獲取到huawei pro 這個(gè)key的值后,準(zhǔn)備去db獲取數(shù)據(jù),此時(shí)獲取db的代碼加了分布式鎖,那么每個(gè)請(qǐng)求,也是每個(gè)線程都會(huì)去獲取huawei pro 的分布式鎖(圖中利用redis實(shí)現(xiàn)了分布式鎖,后面我會(huì)有單獨(dú)一篇文章來介紹分布式鎖的實(shí)現(xiàn),不限于redis)。

獲取鎖之后:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

此時(shí)線程A獲取了huawei pro 的分布式鎖,那么線程A就會(huì)去DB加載數(shù)據(jù),然后由線程A將huawei pro 再次設(shè)置到cache內(nèi),然后返回?cái)?shù)據(jù)。

其他的線程就沒有獲取到,一種方式就是直接返回空值給客戶端,還有一種等待50-100ms ,因?yàn)椴樵僤b和放入redis 會(huì)很快,此時(shí)等待,再次查詢的時(shí)候,結(jié)果可能就有了,如果沒有就直接返回null,當(dāng)然也可以重試,當(dāng)然在大并發(fā)的場(chǎng)景下,還是希望能夠快速的返回結(jié)果,不能發(fā)生太多次數(shù)的重試操作。

3、定時(shí)任務(wù)更新熱點(diǎn)key

這個(gè)就很好理解,說白了,就是一個(gè)定時(shí)任務(wù)定時(shí)的去監(jiān)控某些熱點(diǎn)key的超時(shí)時(shí)間,是否到期,再進(jìn)行快到期了的時(shí)候延長key在cache中的緩存時(shí)間就可以了。

單個(gè)線程輪詢的方式檢查和更新失效時(shí)間,看圖:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

多線程的方式,注意熱點(diǎn)的key 不能太多,某個(gè)線程會(huì)開啟很多,如果熱點(diǎn)key很多,可以采用線程池的方式,看圖:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

延遲隊(duì)列實(shí)現(xiàn)

上面的方式說白了,無論是單個(gè)線程還是多個(gè)線程,都是會(huì)采用輪詢的方式(每次白白浪費(fèi)的cpu),來檢查是否key 快到期了,這種方式檢查會(huì)存在檢查時(shí)間不準(zhǔn)確,可能會(huì)造成時(shí)間的延遲或者不準(zhǔn)確,你在等待進(jìn)行下次檢查的時(shí)候,這個(gè)key就沒了,那么此時(shí)就已經(jīng)發(fā)了擊穿,這個(gè)情況的發(fā)生雖然概率低,但也是有的,那么我們?cè)趺床拍鼙苊饽?,其?shí)咱們可以利用延遲隊(duì)列(環(huán)形隊(duì)列來實(shí)現(xiàn),這里我不深入講這個(gè)隊(duì)列的原理了,大家可以自行百度或者google),所謂的延遲隊(duì)列就是你往這個(gè)隊(duì)列發(fā)送消息,希望按照你設(shè)置的時(shí)間來進(jìn)行消費(fèi),時(shí)間沒到不會(huì)進(jìn)行消費(fèi),時(shí)間到了就進(jìn)行消費(fèi),好了,看圖說話吧:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

1、程序首次啟動(dòng) 獲取名單內(nèi)key的失效時(shí)間。
2、依次設(shè)置key 延遲消費(fèi)的時(shí)間,注意這個(gè)消費(fèi)時(shí)間要比失效時(shí)間要早。
3、延遲隊(duì)列到期,消費(fèi)端進(jìn)行消費(fèi)key。
4、消費(fèi)端消費(fèi)消息,延遲key的失效時(shí)間到cache。
5、再次發(fā)送key 新的失效時(shí)間到延遲隊(duì)列,等待下次延遲cache的失效時(shí)間。

4、設(shè)置key 不失效

這種其實(shí)也可能會(huì)因?yàn)閮?nèi)存不足,key 被淘汰,大家可以想想什么情況下,key 會(huì)被淘汰。

緩存穿透

定義

所謂穿透,就是訪問了一個(gè)cache不存在,數(shù)據(jù)庫里也不存在的key,那么此時(shí)相當(dāng)于流量直接到達(dá)了DB 了,那么一些流氓就可以利用這個(gè)漏洞,瘋狂的刷你的接口,進(jìn)而把你的DB打垮,你的業(yè)務(wù)也就不能正常運(yùn)行了。

如何解決呢?

1、設(shè)置null 或者特殊值

我們可以通過設(shè)置null 或者特定的值到redis內(nèi),且不過期,那么下次再來的時(shí)候,直接從redis 獲取這個(gè)null 或者 特殊值就可以了。

這個(gè)方案不能解決根本性的問題,如果這個(gè)流量能仿造出大量的無用key,你設(shè)置再多的null或者特殊的值都是沒有用的,那么我們應(yīng)該怎么解決呢?

2、布隆過濾器

布隆過濾器 英文為 bloomfiler,這里我們只是做簡單的介紹,介于篇幅的原因,后面會(huì)有單獨(dú)的文章做介紹。
舉個(gè)例子,如果我們數(shù)據(jù)庫里存儲(chǔ)著千萬級(jí)別的sku 數(shù)據(jù),我們現(xiàn)在的需求是如果庫有這個(gè)sku,那么就查詢r(jià)edis ,如果redis 沒有就查詢數(shù)據(jù)庫,然后更新redis,我們最先想到的就是把sku數(shù)據(jù)放入到一hashmap內(nèi),key 就是sku,因?yàn)閟ku 的數(shù)量很多,那么這個(gè)hashmap占用的內(nèi)存空間會(huì)很大,有可能會(huì)撐爆內(nèi)存,最后得不償失了,那么怎么來節(jié)省內(nèi)存,我們可以利用一個(gè)bit的數(shù)組,來存儲(chǔ)這個(gè)sku是否存在狀態(tài),0 代表不存在,1 代表存在,我們可以利用一個(gè)散列函數(shù),算出sku的散列值,然后sku的散列值對(duì)bit數(shù)組進(jìn)行取模,找到所在數(shù)組的位置,然后設(shè)置為1,當(dāng)請(qǐng)求來的時(shí)候,我們會(huì)算出這個(gè)sku 散列值對(duì)應(yīng)的數(shù)組位置是否為1 ,為1 說明就存在,為0 說明就不存在。這樣一個(gè)簡單的bloomfilter就實(shí)現(xiàn)了,bloomfiler 是有錯(cuò)誤率,可以考慮增加數(shù)組長度和散列函數(shù)的數(shù)量來提供準(zhǔn)確率,具體可以百度或者google,今天在這里就不講了。

下面看看利用bloomfiler 來防止緩存穿透的流程,看圖說話:

bloomfiler的初始化 可以通過一個(gè)定時(shí)任務(wù)來讀取 db,初始化bit數(shù)組的大小,默認(rèn)值都是為0,表示不存在,然后每條都計(jì)算散列值對(duì)應(yīng)的數(shù)組位置,然后插入到bit 數(shù)組中。

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

請(qǐng)求流程,看圖:

Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析

如果不利用bloomfiler 過濾器,對(duì)于一個(gè)數(shù)據(jù)庫里根本不存在的key,其實(shí)白白浪費(fèi)了兩次IO,一次查詢r(jià)edis,一次查詢DB,有了bloomfiler ,那么就節(jié)省了這兩次無用的IO,減少后端redis 和 DB 資源的浪費(fèi)。

總結(jié)

緩存雪崩

解決方案:

  • 在設(shè)置失效時(shí)間段的時(shí)候,加上一個(gè)時(shí)間的隨機(jī)數(shù),可以幾分鐘之內(nèi)的都可以。

  • 以及如果真的雪崩了怎么辦的問題,可以采用限流的方式。

緩存擊穿

解決方案:

  • 限流

  • 分布式鎖

  • 定時(shí)更新熱點(diǎn)key  ,這里重點(diǎn)看下延遲隊(duì)列。

  • 設(shè)置時(shí)間不失效

緩存穿透

解決方案:

  • 設(shè)置null 或者特定的值到redis

  • 使用bloomfiler實(shí)現(xiàn)

以上是“Redis中緩存雪崩、緩存擊穿和緩存穿透的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

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

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

AI