摘要:不過,布隆過濾器有一個(gè)最大的缺點(diǎn),也是其為了高效利用內(nèi)存而付出的代價(jià),就是無法確保的準(zhǔn)確率。不過這種方式的優(yōu)勢(shì)是前面提到的,不會(huì)出現(xiàn)誤差,而布隆過濾器的錯(cuò)誤率會(huì)隨著位數(shù)的增加而減少,會(huì)不斷趨近于,但不會(huì)為。
?如果第二次看到我的文章,歡迎文末掃碼訂閱我個(gè)人的公眾號(hào)(跨界架構(gòu)師)喲~
本文長(zhǎng)度為2805字,建議閱讀8分鐘。
堅(jiān)持原創(chuàng),每一篇都是用心之作~
有句話說得好,欲要使其毀滅,先要使其瘋狂。當(dāng)你沉浸在緩存所帶來的系統(tǒng)tps飆升的喜悅中時(shí),使你系統(tǒng)毀滅的種子也已經(jīng)埋在其中。
而且,你所承載的tps越高,它所帶來的毀滅性更大。
在前兩篇《360°全方位解讀「緩存」》和《先寫DB還是「緩存」?》中,我們已經(jīng)對(duì)緩存有了一定的認(rèn)識(shí),并且知道了關(guān)于緩存相關(guān)的「一致性」問題的最佳實(shí)踐。
這次,我們就來聊聊隱藏在緩存中的毀滅性種子是什么?
我們從前一篇文章《先寫DB還是「緩存」?》中多次提到的「cache miss」說起。
在前一篇文章《先寫DB還是「緩存」?》中,我們多次提到了「cache miss」這個(gè)詞,利用「cache miss」來更好的保障DB和緩存之間的數(shù)據(jù)一致性。
然而,任何事物都是有兩面性的,「cache miss」在提供便利的同時(shí),也帶來了一個(gè)潛在風(fēng)險(xiǎn)。
這個(gè)風(fēng)險(xiǎn)就是「緩存雪崩」。
在圖中的第二步,大量的請(qǐng)求并發(fā)進(jìn)入,這里的一次「cache miss」就有可能導(dǎo)致產(chǎn)生「緩存雪崩」。
不過,雖然「cache miss」會(huì)產(chǎn)生「緩存雪崩」,但「緩存雪崩」并不僅僅產(chǎn)生于「cache miss」。
雪崩一詞源于「雪崩效應(yīng)」,是指像「多米勒骨牌」這樣的級(jí)聯(lián)反應(yīng)。前面沒頂住,導(dǎo)致影響后面,如此蔓延。(關(guān)于對(duì)應(yīng)雪崩的方式參考之前的文章,文末放鏈接)
所以「緩存雪崩」的根本問題是:緩存由于某些原因未起到預(yù)期的緩沖效果,導(dǎo)致請(qǐng)求全部流轉(zhuǎn)到數(shù)據(jù)庫(kù),造成數(shù)據(jù)庫(kù)壓力過重。
因此,流量激增、高并發(fā)下的緩存過期、甚至緩存系統(tǒng)宕機(jī)都有可能產(chǎn)生「緩存雪崩」問題。
怎么解決這個(gè)問題呢?宕機(jī)可以通過做高可用來解決(可以參考之前的文章,文末放鏈接)。而在“流量激增”、“高并發(fā)下的緩存過期”這兩種場(chǎng)景下,也有兩種方式可以來解決。
通過加鎖或者排隊(duì)機(jī)制來限制讀數(shù)據(jù)庫(kù)寫緩存的線程數(shù)量。比如,下面的偽代碼就是對(duì)某個(gè)key只允許一個(gè)線程進(jìn)入的效果。
key = "aaa"; ? var cacheValue = cache.read(key); if (cacheValue != null) { return cacheValue; } else { lock(key) { cacheValue = cache.read(key); if (cacheValue != null) { return cacheValue; } else { cacheValue = db.read(key); cache.set(key,cacheValue); } } return cacheValue; }
這個(gè)比較好理解,就不廢話了。
這個(gè)主要針對(duì)的是「緩存定時(shí)過期」機(jī)制下的取巧方案。它的目的是避免多個(gè)緩存key在同一時(shí)間失效,導(dǎo)致壓力更加集中。
比如,你有10個(gè)key,他們的過期時(shí)間都是30分鐘的話,那么30分鐘后這10個(gè)key的所有請(qǐng)求會(huì)同時(shí)流到db去。
而這里說的這種方式就是將這10個(gè)key的過期時(shí)間打亂,比如設(shè)置成25、26、27、...、34分的過期時(shí)間,這樣壓力就被分散了,每分鐘只有一個(gè)key過期。
最簡(jiǎn)單粗暴的方式就是在設(shè)置「過期時(shí)間」的時(shí)候加一個(gè)隨機(jī)數(shù)字。
cache.set(key,cacheValue,30+random())
總體來看,相比后者,前者的適用面更廣,所以Z哥建議你用「加鎖排隊(duì)」作為默認(rèn)的通用方案不失為一個(gè)不錯(cuò)的選擇。
如果你聽說過「緩存穿透」的話,可能會(huì)問:「緩存雪崩」和「緩存穿透」一樣嗎?
從產(chǎn)生的效果上看是一樣的,但是過程不同。
來舉個(gè)例子。例子純屬虛構(gòu),別太在意合理性~
在一個(gè)方圓一萬里的地區(qū)內(nèi),只有一個(gè)修手機(jī)的老師傅。他收了一個(gè)徒弟,希望徒弟能幫他分擔(dān)掉一部分的工作壓力。這里的老師傅可以看作是DB,徒弟看作是緩存。老師傅對(duì)徒弟說,如果遇到你不會(huì)做的事你來請(qǐng)教我。
然后,一個(gè)客戶過來說要修一下他的衛(wèi)星電話,徒弟去請(qǐng)教老師傅,老師傅說他也不會(huì),先拒絕了吧。
但是由于沒告訴他后續(xù)遇到修衛(wèi)星電話的人該怎么做,所以后續(xù)這個(gè)客戶一直來問,徒弟每次都又去請(qǐng)教老師傅。最終,在修衛(wèi)星電話這件事上,徒弟并沒有幫老師傅緩解任何的壓力,快被煩死了。
上面這個(gè)故事就好比「緩存穿透」。
而「緩存雪崩」則是,由于徒弟年輕力壯,精力充沛,1小時(shí)能修20個(gè)手機(jī),老師傅只能修10個(gè)(但是手藝好,更考究)。
然后,有一天徒弟請(qǐng)假了,但恰巧這天來了2000個(gè)修手機(jī)的,老師傅修不過來就被累垮了。
所以,「緩存穿透」和「緩存雪崩」最終產(chǎn)生的效果是一樣的,就是因?yàn)榇罅空?qǐng)求流到DB后,把DB拖垮(正如前面故事中的老師傅)。
兩者最大的不同在于,「緩存雪崩」問題只要數(shù)據(jù)從db中找到并放入緩存就能恢復(fù)正常(徒弟休假歸來),而「緩存穿透」指的是所需的數(shù)據(jù)在DB中一直不存在的情況(老師傅也不會(huì)修)。并且,由于DB中數(shù)據(jù)不存在,所以自然每次從緩存中也找不到(徒弟也不會(huì)修)。
清楚了兩者的區(qū)別之后,我們下面就來聊聊「緩存穿透」的常見應(yīng)對(duì)方式。
「緩存穿透」有時(shí)也叫做「緩存擊穿」,產(chǎn)生的邏輯過程是這樣,一直在虛線范圍內(nèi)流轉(zhuǎn)。
在這種場(chǎng)景下,緩存的作用完全失效,每次請(qǐng)求都“穿透”到了DB中。
可能你會(huì)想,為什么會(huì)存在大量的這種db中數(shù)據(jù)不存在的情況呢?其實(shí),任何依賴外部參數(shù)進(jìn)行查詢的地方都可能有這個(gè)問題的存在。比如,一個(gè)文本輸入框,本來是讓你輸入用戶名的,但是手誤輸入了密碼,自然就找不到數(shù)據(jù)咯。更主要的問題是,會(huì)有惡意分子利用這種機(jī)制來對(duì)你的系統(tǒng)進(jìn)行攻擊,擊穿緩存搞垮你的數(shù)據(jù)庫(kù),導(dǎo)致整個(gè)系統(tǒng)全面癱瘓。
同樣也有兩種方式來解決這個(gè)問題。
布隆過濾器就是由一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)組成,將確定不存在的數(shù)據(jù)構(gòu)建到過濾器中,用它來過濾請(qǐng)求。這里就放個(gè)圖,具體就不展開了,后續(xù)我們?cè)倭模ㄓ信d趣的可以先到搜索引擎搜《Space time trade-offs in hash coding with allowable errors》找到bloom的原始論文)。
實(shí)現(xiàn)代碼其實(shí)并不很復(fù)雜,參考論文或者網(wǎng)上其他作者的一些實(shí)現(xiàn)就可以寫出來。
不過,布隆過濾器有一個(gè)最大的缺點(diǎn),也是其為了高效利用內(nèi)存而付出的代價(jià),就是無法確保100%的準(zhǔn)確率。
所以,如果你的場(chǎng)景要求是100%準(zhǔn)確的,就只能用下面這種方式了。
其實(shí)就是哪怕從db中取出的數(shù)據(jù)是“空(null)”,也把它丟失到緩存中。
這樣一來,雖然緩存中存在著一個(gè)value為空的數(shù)據(jù),但是至少他能表示“數(shù)據(jù)庫(kù)里也沒有不用找了”。
其實(shí)這個(gè)思路和布隆過濾器有些類似,但是它對(duì)內(nèi)存的消耗會(huì)大很多,畢竟布隆過濾器是利用的bit位來存儲(chǔ)。不過這種方式的優(yōu)勢(shì)是前面提到的,不會(huì)出現(xiàn)誤差,而布隆過濾器的錯(cuò)誤率會(huì)隨著「位數(shù)」的增加而減少,會(huì)不斷趨近于0,但不會(huì)為0。
好了,我們一起總結(jié)一下。
這次呢,Z哥主要和你聊了隱藏在緩存中的兩顆具有“毀滅性”的種子,「緩存雪崩」和「緩存穿透」,以及應(yīng)對(duì)這兩顆種子的常用方式。
而且,順便幫你區(qū)分清楚了「緩存雪崩」和「緩存穿透」的差異。
希望對(duì)你有所啟發(fā)。
相關(guān)文章:
分布式系統(tǒng)關(guān)注點(diǎn)——360°全方位解讀「緩存」
分布式系統(tǒng)關(guān)注點(diǎn)——先寫DB還是「緩存」?
作者:Zachary
出處:https://www.cnblogs.com/Zacha...
如果你喜歡這篇文章,可以點(diǎn)一下文末的「贊」。
這樣可以給我一點(diǎn)反饋。: )
謝謝你的舉手之勞。
?關(guān)于作者:張帆(Zachary,個(gè)人微信號(hào):Zachary-ZF)。堅(jiān)持用心打磨每一篇高質(zhì)量原創(chuàng)。歡迎掃描下方的二維碼~。
定期發(fā)表原創(chuàng)內(nèi)容:架構(gòu)設(shè)計(jì)丨分布式系統(tǒng)丨產(chǎn)品丨運(yùn)營(yíng)丨一些思考。如果你是初級(jí)程序員,想提升但不知道如何下手。又或者做程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「技術(shù)」,送你一份我長(zhǎng)期收集和整理的思維導(dǎo)圖。
如果你是運(yùn)營(yíng),面對(duì)不斷變化的市場(chǎng)束手無策。又或者想了解主流的運(yùn)營(yíng)策略,以豐富自己的“倉(cāng)庫(kù)”。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「運(yùn)營(yíng)」,送你一份我長(zhǎng)期收集和整理的思維導(dǎo)圖。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/62111.html
摘要:關(guān)于緩存熱點(diǎn)重建原文說到在緩存失效的瞬間,有大量線程來重建緩存,造成后端負(fù)載加大,甚至可能會(huì)讓應(yīng)用崩潰,并給出互斥鎖和永遠(yuǎn)不過期兩種候選方案。即使繞過互斥鎖,也不會(huì)產(chǎn)生什么不好的后果,因?yàn)楦戮彺媸且粋€(gè)冪等操作。 向大家推薦這篇文章——Redis架構(gòu)之防雪崩設(shè)計(jì):網(wǎng)站不宕機(jī)背后的兵法 (另外推薦我去年的短文作為餐前點(diǎn)心——略談服務(wù)端緩存設(shè)計(jì)) 《Redis架構(gòu)之防雪崩設(shè)計(jì)》這篇文章(下...
閱讀 1705·2021-11-02 14:42
閱讀 555·2021-10-18 13:24
閱讀 1024·2021-10-12 10:12
閱讀 1849·2021-09-02 15:41
閱讀 3242·2019-08-30 15:56
閱讀 2900·2019-08-29 16:09
閱讀 2085·2019-08-29 11:13
閱讀 3654·2019-08-28 18:06