App下載

關(guān)于 緩存穿透/緩存擊穿/緩存雪崩 看這篇文章就夠了

猿友 2020-10-13 16:23:40 瀏覽數(shù) (2514)
反饋

國(guó)慶加中秋過(guò)去了,大家準(zhǔn)備好學(xué)習(xí)了么?

redis 在項(xiàng)目中用的話,主要就是用作緩存了

既然用作緩存,那就肯定會(huì)有 緩存穿透/緩存擊穿/緩存雪崩 的問(wèn)題

這篇文章就來(lái)說(shuō)說(shuō),遇到這種情況時(shí),該如何去處理

緩存穿透

首先咱們搞明白什么是緩存穿透?這三個(gè)詞這么像,得把概念搞清楚不是

其實(shí)只是從字面意思上來(lái)看的話,大概也能知道一點(diǎn)兒,緩存穿透嘛,就是直接穿過(guò)了緩存,將請(qǐng)求打到了數(shù)據(jù)庫(kù)上面去

一般情況下,去查詢數(shù)據(jù)的話,緩存里面應(yīng)該都是有的,但是防不住黑客呀,如果黑客請(qǐng)求查詢的是數(shù)據(jù)庫(kù)里面根本不存在的數(shù)據(jù),數(shù)據(jù)庫(kù)里面都沒(méi)有的數(shù)據(jù),緩存里面肯定也不會(huì)有了,對(duì)吧,那么此時(shí)請(qǐng)求就會(huì)打到咱們的數(shù)據(jù)庫(kù)里面去,這就是緩存穿透

你想啊,黑客想要攻擊的話,怎么可能只請(qǐng)求一次呢,肯定是大量的請(qǐng)求過(guò)來(lái),因?yàn)槭悄脭?shù)據(jù)庫(kù)里面不存在的 id 來(lái)請(qǐng)求的,那么這些請(qǐng)求毫無(wú)疑問(wèn)直接打到了數(shù)據(jù)庫(kù)上面去,那咱們的數(shù)據(jù)庫(kù)可能就會(huì)因?yàn)檫@些大量的請(qǐng)求直接宕掉

如何解決呢?

咱們回到產(chǎn)生這個(gè)問(wèn)題的場(chǎng)景中,為什么大量的請(qǐng)求會(huì)打到數(shù)據(jù)庫(kù)上面來(lái)?因?yàn)榫彺胬锩鏇](méi)有對(duì)應(yīng)的 key 對(duì)吧,所以才會(huì)越過(guò)緩存直接到數(shù)據(jù)庫(kù)

那么問(wèn)題就好解決了嘛,緩存里面沒(méi)有對(duì)應(yīng)的 key ?OK ,如果這個(gè) key 數(shù)據(jù)庫(kù)里面也沒(méi)有,那我就在 redis 里面,存上這個(gè) key ,值是 null ,這樣如果有查詢這個(gè) key 的請(qǐng)求,我直接返回 null 就完事兒了,也就不用打到數(shù)據(jù)庫(kù)上面去了

注意一下,要記得設(shè)置它的過(guò)期時(shí)間,一般三到五分鐘就夠了

但是對(duì)方是個(gè)黑客呀,可能就用一個(gè) key 去請(qǐng)求么?他可能會(huì)在短時(shí)間內(nèi)用大量的 key 來(lái)發(fā)送請(qǐng)求,那如果一個(gè) key 就在 redis 中存儲(chǔ)一個(gè) null 值的話,那么多 key 是不是就會(huì)存儲(chǔ)那么多個(gè) null 值嘞?

這樣的話, redis 里面是不是都是值為 null 的了?

所以有沒(méi)有更好的解決辦法呢?

那必須得有!布隆過(guò)濾器,你值得嘗試

什么是布隆過(guò)濾器呢?就是它能告訴你,某個(gè)值一定不存在或者可能存在( emmmm ,也不知道我有沒(méi)有說(shuō)清楚

所以可以將數(shù)據(jù)庫(kù)的內(nèi)容緩存一份到布隆過(guò)濾器,這樣的話,當(dāng)大量的請(qǐng)求過(guò)來(lái)的時(shí)候, redis 里面沒(méi)有,沒(méi)關(guān)系,再去布隆過(guò)濾器過(guò)濾一下,這樣請(qǐng)求不用打到數(shù)據(jù)庫(kù)上面去,就能確定這個(gè) key 數(shù)據(jù)庫(kù)中有沒(méi)有

這樣不就降低了數(shù)據(jù)庫(kù)的壓力么,可真是個(gè)天才~

緩存擊穿

緩存擊穿說(shuō)的是,在高并發(fā)情況下,如果好多個(gè)請(qǐng)求都在查詢一個(gè) key ,好巧不巧的是,這個(gè) key 因?yàn)槟承┰蚴Я耍ū热缭O(shè)置的過(guò)期時(shí)間到了,緩存服務(wù)器宕機(jī)了),這樣就會(huì)導(dǎo)致那么多的請(qǐng)求都直接打到數(shù)據(jù)庫(kù)上面去了

那如果這些請(qǐng)求的數(shù)量足夠大的話,可能直接把數(shù)據(jù)庫(kù)就干掉了

知道了造成結(jié)果的原因,那么尋找解決方案也就好辦了

不是因?yàn)楹枚鄠€(gè)請(qǐng)求打到了數(shù)據(jù)庫(kù)嘛,但是它們請(qǐng)求的都只是一個(gè) key ,所以這里可以使用排斥鎖來(lái)實(shí)現(xiàn),第一個(gè)請(qǐng)求達(dá)到請(qǐng)求 key 發(fā)現(xiàn)緩存里面沒(méi)有,允許它去數(shù)據(jù)庫(kù)查詢,同時(shí)加鎖,這樣第二個(gè)請(qǐng)求,第三個(gè)請(qǐng)求…都會(huì)被鎖阻塞到當(dāng)前,不會(huì)再打到數(shù)據(jù)庫(kù),這樣就減少了數(shù)據(jù)庫(kù)的并發(fā)壓力

緩存雪崩

緩存雪崩,雪崩雪崩嘛,就比較嚴(yán)重,擊穿說(shuō)的是一個(gè) key 失效的情況,雪崩指的是大規(guī)模的緩存失效情況的發(fā)生,這是有可能發(fā)生的,比如說(shuō)我的緩存服務(wù)器宕機(jī)了,那是不是直接就大規(guī)模的緩存失效了;或者說(shuō),我當(dāng)時(shí)為了圖省事,好多個(gè) key 設(shè)置的過(guò)期時(shí)間都是一樣的,然后剛好在緩存都失效的時(shí)候,好多請(qǐng)求不同的 key 過(guò)來(lái)了

解決方案的話,其實(shí)就不適合使用加鎖的方式去解決了,因?yàn)檫@是好多請(qǐng)求不同的 key ,它不是一個(gè)嘛

而且嘞,咱們是因?yàn)楹枚鄠€(gè) key 設(shè)置的過(guò)期時(shí)間都是一樣的,所以解決方案就是,咱們不設(shè)置同樣的時(shí)間讓緩存失效了,咱們給一個(gè)隨機(jī)時(shí)間,讓緩存隨機(jī)失效,這樣的話,大規(guī)模的緩存失效情況就減少很多了

那還要一種情況呢,就是如果我的緩存服務(wù)器直接宕機(jī)了,這怎么辦?也好弄,來(lái)個(gè)集群就解決了,這里只是一個(gè)解決方案,它的落地實(shí)現(xiàn)不是本文重點(diǎn)哈~

再談 布隆過(guò)濾器

OK ,你如果看到這里的話,其實(shí)這篇文章的內(nèi)容就說(shuō)完了

但是我感覺(jué)布隆過(guò)濾器那塊,我沒(méi)有說(shuō)清楚,所以在這里拿出來(lái)詳細(xì)說(shuō)一說(shuō)(我知道你一定又在默默夸阿粉是個(gè)暖男了,乖,知道就好了,不要真說(shuō)出來(lái),我會(huì)害羞的

布隆過(guò)濾器是一種數(shù)據(jù)結(jié)構(gòu),它是一種概率型的數(shù)據(jù)結(jié)構(gòu),就是它能告訴你“某樣?xùn)|西一定不存在或者可能存在”

你可能會(huì)說(shuō),這話剛剛不是說(shuō)過(guò)了嘛,本來(lái)就挺拗口的,你咋還說(shuō)

還不是因?yàn)檫@句話比較重要,我覺(jué)得把這句話理解透徹了,那么對(duì)布隆過(guò)濾器理解的應(yīng)該也就到位了

來(lái),為了形象生動(dòng)一些,咱們舉個(gè)例子~ 布隆過(guò)濾器是一個(gè) bit 向量或者說(shuō) bit 數(shù)組,大概長(zhǎng)這樣:

布隆過(guò)濾器

現(xiàn)在,我們需要把 “AliPay” 這個(gè)字段給存儲(chǔ)進(jìn)去 大概的存儲(chǔ)過(guò)程就是:將要映射的值,使用多個(gè)不同的哈希函數(shù)生成多個(gè)哈希值,然后每個(gè)生成的哈希值指向的 bit 置為 1

以給的為例,我們現(xiàn)在將 “AliPay” 這個(gè)值,通過(guò)三個(gè)不同的哈希函數(shù)進(jìn)行映射,那么大概就是這樣了:

三個(gè)不同的哈希函數(shù)進(jìn)行映射

同樣,現(xiàn)在我要存儲(chǔ)另外一個(gè)值 “WechatPay” ,那么可能映射之后就是下面這樣:

WechatPay

細(xì)心的你可能就會(huì)發(fā)現(xiàn), 4 號(hào)位置的值,剛開(kāi)始不是給 “AliPay” 了么,后來(lái) “WechatPay” 也在那里,這樣的話,值不就給覆蓋掉了嘛

嗯,沒(méi)錯(cuò),是被覆蓋掉了

接下來(lái),我們查詢 “Ali” 那么查詢之后,布隆過(guò)濾器可能會(huì)給你 “0,1,2” 的值, 結(jié)果呢 “2” 的位置是 0 ,說(shuō)明沒(méi)有任何值映射到這個(gè)位置上來(lái),所以我們就可以判定數(shù)據(jù)庫(kù)里面沒(méi)有 “Ali” 這個(gè)值

那我查詢 “AliPay” 的話,毫無(wú)疑問(wèn),肯定會(huì)返回給我 “1,4,6” ,那我們能說(shuō)數(shù)據(jù)庫(kù)里面一定有 “AliPay” 么?不能,因?yàn)?“1,4,6” 的值有可能被其他的值給覆蓋到了,所以我們只能說(shuō),數(shù)據(jù)庫(kù)里可能存在 “AliPay”

這就是布隆過(guò)濾器說(shuō)的"某個(gè)值一定不存在或者可能存在"

乖,你懂了嗎?

文章來(lái)源于公眾號(hào):Java極客技術(shù) 作者:鴨血粉絲

以上就是W3Cschool編程獅關(guān)于關(guān)于 緩存穿透/緩存擊穿/緩存雪崩 看這篇文章就夠了的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊