國慶加中秋過去了,大家準(zhǔn)備好學(xué)習(xí)了么?
redis 在項(xiàng)目中用的話,主要就是用作緩存了
既然用作緩存,那就肯定會(huì)有 緩存穿透/緩存擊穿/緩存雪崩 的問題
這篇文章就來說說,遇到這種情況時(shí),該如何去處理
緩存穿透
首先咱們搞明白什么是緩存穿透?這三個(gè)詞這么像,得把概念搞清楚不是
其實(shí)只是從字面意思上來看的話,大概也能知道一點(diǎn)兒,緩存穿透嘛,就是直接穿過了緩存,將請(qǐng)求打到了數(shù)據(jù)庫上面去
一般情況下,去查詢數(shù)據(jù)的話,緩存里面應(yīng)該都是有的,但是防不住黑客呀,如果黑客請(qǐng)求查詢的是數(shù)據(jù)庫里面根本不存在的數(shù)據(jù),數(shù)據(jù)庫里面都沒有的數(shù)據(jù),緩存里面肯定也不會(huì)有了,對(duì)吧,那么此時(shí)請(qǐng)求就會(huì)打到咱們的數(shù)據(jù)庫里面去,這就是緩存穿透
你想啊,黑客想要攻擊的話,怎么可能只請(qǐng)求一次呢,肯定是大量的請(qǐng)求過來,因?yàn)槭悄脭?shù)據(jù)庫里面不存在的 id 來請(qǐng)求的,那么這些請(qǐng)求毫無疑問直接打到了數(shù)據(jù)庫上面去,那咱們的數(shù)據(jù)庫可能就會(huì)因?yàn)檫@些大量的請(qǐng)求直接宕掉
如何解決呢?
咱們回到產(chǎn)生這個(gè)問題的場景中,為什么大量的請(qǐng)求會(huì)打到數(shù)據(jù)庫上面來?因?yàn)榫彺胬锩鏇]有對(duì)應(yīng)的 key 對(duì)吧,所以才會(huì)越過緩存直接到數(shù)據(jù)庫
那么問題就好解決了嘛,緩存里面沒有對(duì)應(yīng)的 key ?OK ,如果這個(gè) key 數(shù)據(jù)庫里面也沒有,那我就在 redis 里面,存上這個(gè) key ,值是 null ,這樣如果有查詢這個(gè) key 的請(qǐng)求,我直接返回 null 就完事兒了,也就不用打到數(shù)據(jù)庫上面去了
注意一下,要記得設(shè)置它的過期時(shí)間,一般三到五分鐘就夠了
但是對(duì)方是個(gè)黑客呀,可能就用一個(gè) key 去請(qǐng)求么?他可能會(huì)在短時(shí)間內(nèi)用大量的 key 來發(fā)送請(qǐng)求,那如果一個(gè) key 就在 redis 中存儲(chǔ)一個(gè) null 值的話,那么多 key 是不是就會(huì)存儲(chǔ)那么多個(gè) null 值嘞?
這樣的話, redis 里面是不是都是值為 null 的了?
所以有沒有更好的解決辦法呢?
那必須得有!布隆過濾器,你值得嘗試
什么是布隆過濾器呢?就是它能告訴你,某個(gè)值一定不存在或者可能存在( emmmm ,也不知道我有沒有說清楚
所以可以將數(shù)據(jù)庫的內(nèi)容緩存一份到布隆過濾器,這樣的話,當(dāng)大量的請(qǐng)求過來的時(shí)候, redis 里面沒有,沒關(guān)系,再去布隆過濾器過濾一下,這樣請(qǐng)求不用打到數(shù)據(jù)庫上面去,就能確定這個(gè) key 數(shù)據(jù)庫中有沒有
這樣不就降低了數(shù)據(jù)庫的壓力么,可真是個(gè)天才~
緩存擊穿
緩存擊穿說的是,在高并發(fā)情況下,如果好多個(gè)請(qǐng)求都在查詢一個(gè) key ,好巧不巧的是,這個(gè) key 因?yàn)槟承┰蚴Я耍ū热缭O(shè)置的過期時(shí)間到了,緩存服務(wù)器宕機(jī)了),這樣就會(huì)導(dǎo)致那么多的請(qǐng)求都直接打到數(shù)據(jù)庫上面去了
那如果這些請(qǐng)求的數(shù)量足夠大的話,可能直接把數(shù)據(jù)庫就干掉了
知道了造成結(jié)果的原因,那么尋找解決方案也就好辦了
不是因?yàn)楹枚鄠€(gè)請(qǐng)求打到了數(shù)據(jù)庫嘛,但是它們請(qǐng)求的都只是一個(gè) key ,所以這里可以使用排斥鎖來實(shí)現(xiàn),第一個(gè)請(qǐng)求達(dá)到請(qǐng)求 key 發(fā)現(xiàn)緩存里面沒有,允許它去數(shù)據(jù)庫查詢,同時(shí)加鎖,這樣第二個(gè)請(qǐng)求,第三個(gè)請(qǐng)求…都會(huì)被鎖阻塞到當(dāng)前,不會(huì)再打到數(shù)據(jù)庫,這樣就減少了數(shù)據(jù)庫的并發(fā)壓力
緩存雪崩
緩存雪崩,雪崩雪崩嘛,就比較嚴(yán)重,擊穿說的是一個(gè) key 失效的情況,雪崩指的是大規(guī)模的緩存失效情況的發(fā)生,這是有可能發(fā)生的,比如說我的緩存服務(wù)器宕機(jī)了,那是不是直接就大規(guī)模的緩存失效了;或者說,我當(dāng)時(shí)為了圖省事,好多個(gè) key 設(shè)置的過期時(shí)間都是一樣的,然后剛好在緩存都失效的時(shí)候,好多請(qǐng)求不同的 key 過來了
解決方案的話,其實(shí)就不適合使用加鎖的方式去解決了,因?yàn)檫@是好多請(qǐng)求不同的 key ,它不是一個(gè)嘛
而且嘞,咱們是因?yàn)楹枚鄠€(gè) key 設(shè)置的過期時(shí)間都是一樣的,所以解決方案就是,咱們不設(shè)置同樣的時(shí)間讓緩存失效了,咱們給一個(gè)隨機(jī)時(shí)間,讓緩存隨機(jī)失效,這樣的話,大規(guī)模的緩存失效情況就減少很多了
那還要一種情況呢,就是如果我的緩存服務(wù)器直接宕機(jī)了,這怎么辦?也好弄,來個(gè)集群就解決了,這里只是一個(gè)解決方案,它的落地實(shí)現(xiàn)不是本文重點(diǎn)哈~
再談 布隆過濾器
OK ,你如果看到這里的話,其實(shí)這篇文章的內(nèi)容就說完了
但是我感覺布隆過濾器那塊,我沒有說清楚,所以在這里拿出來詳細(xì)說一說(我知道你一定又在默默夸阿粉是個(gè)暖男了,乖,知道就好了,不要真說出來,我會(huì)害羞的
布隆過濾器是一種數(shù)據(jù)結(jié)構(gòu),它是一種概率型的數(shù)據(jù)結(jié)構(gòu),就是它能告訴你“某樣?xùn)|西一定不存在或者可能存在”
你可能會(huì)說,這話剛剛不是說過了嘛,本來就挺拗口的,你咋還說
還不是因?yàn)檫@句話比較重要,我覺得把這句話理解透徹了,那么對(duì)布隆過濾器理解的應(yīng)該也就到位了
來,為了形象生動(dòng)一些,咱們舉個(gè)例子~ 布隆過濾器是一個(gè) bit 向量或者說 bit 數(shù)組,大概長這樣:
現(xiàn)在,我們需要把 “AliPay” 這個(gè)字段給存儲(chǔ)進(jìn)去 大概的存儲(chǔ)過程就是:將要映射的值,使用多個(gè)不同的哈希函數(shù)生成多個(gè)哈希值,然后每個(gè)生成的哈希值指向的 bit 置為 1
以給的為例,我們現(xiàn)在將 “AliPay” 這個(gè)值,通過三個(gè)不同的哈希函數(shù)進(jìn)行映射,那么大概就是這樣了:
同樣,現(xiàn)在我要存儲(chǔ)另外一個(gè)值 “WechatPay” ,那么可能映射之后就是下面這樣:
細(xì)心的你可能就會(huì)發(fā)現(xiàn), 4 號(hào)位置的值,剛開始不是給 “AliPay” 了么,后來 “WechatPay” 也在那里,這樣的話,值不就給覆蓋掉了嘛
嗯,沒錯(cuò),是被覆蓋掉了
接下來,我們查詢 “Ali” 那么查詢之后,布隆過濾器可能會(huì)給你 “0,1,2” 的值, 結(jié)果呢 “2” 的位置是 0 ,說明沒有任何值映射到這個(gè)位置上來,所以我們就可以判定數(shù)據(jù)庫里面沒有 “Ali” 這個(gè)值
那我查詢 “AliPay” 的話,毫無疑問,肯定會(huì)返回給我 “1,4,6” ,那我們能說數(shù)據(jù)庫里面一定有 “AliPay” 么?不能,因?yàn)?“1,4,6” 的值有可能被其他的值給覆蓋到了,所以我們只能說,數(shù)據(jù)庫里可能存在 “AliPay”
這就是布隆過濾器說的"某個(gè)值一定不存在或者可能存在"
乖,你懂了嗎?
文章來源于公眾號(hào):Java極客技術(shù) 作者:鴨血粉絲
以上就是W3Cschool編程獅
關(guān)于關(guān)于 緩存穿透/緩存擊穿/緩存雪崩 看這篇文章就夠了的相關(guān)介紹了,希望對(duì)大家有所幫助。