原文出處:https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md
歡迎 star 關(guān)注 GitHub 項(xiàng)目最新動(dòng)態(tài)踱卵!
面試題
了解什么是 redis 的雪崩瓤漏、穿透和擊穿?redis 崩潰之后會(huì)怎么樣?系統(tǒng)該如何應(yīng)對(duì)這種情況蔬充?如何處理 redis 的穿透?
面試官心理分析
其實(shí)這是問(wèn)到緩存必問(wèn)的班利,因?yàn)榫彺嫜┍篮痛┩讣⒙蔷彺孀畲蟮膬蓚€(gè)問(wèn)題,要么不出現(xiàn)罗标,一旦出現(xiàn)就是致命性的問(wèn)題庸队,所以面試官一定會(huì)問(wèn)你。
面試題剖析
緩存雪崩
對(duì)于系統(tǒng) A闯割,假設(shè)每天高峰期每秒 5000 個(gè)請(qǐng)求彻消,本來(lái)緩存在高峰期可以扛住每秒 4000 個(gè)請(qǐng)求,但是緩存機(jī)器意外發(fā)生了全盤(pán)宕機(jī)宙拉。緩存掛了宾尚,此時(shí) 1 秒 5000 個(gè)請(qǐng)求全部落數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)必然扛不住谢澈,它會(huì)報(bào)一下警煌贴,然后就掛了。此時(shí)锥忿,如果沒(méi)有采用什么特別的方案來(lái)處理這個(gè)故障牛郑,DBA 很著急,重啟數(shù)據(jù)庫(kù)敬鬓,但是數(shù)據(jù)庫(kù)立馬又被新的流量給打死了淹朋。
這就是緩存雪崩。
大約在 3 年前钉答,國(guó)內(nèi)比較知名的一個(gè)互聯(lián)網(wǎng)公司础芍,曾因?yàn)榫彺媸鹿剩瑢?dǎo)致雪崩希痴,后臺(tái)系統(tǒng)全部崩潰者甲,事故從當(dāng)天下午持續(xù)到晚上凌晨 3~4 點(diǎn),公司損失了幾千萬(wàn)砌创。
緩存雪崩的事前事中事后的解決方案如下虏缸。
- 事前:redis 高可用,主從+哨兵嫩实,redis cluster刽辙,避免全盤(pán)崩潰。
- 事中:本地 ehcache 緩存 + hystrix 限流&降級(jí)甲献,避免 MySQL 被打死宰缤。
- 事后:redis 持久化,一旦重啟,自動(dòng)從磁盤(pán)上加載數(shù)據(jù)慨灭,快速恢復(fù)緩存數(shù)據(jù)朦乏。
用戶(hù)發(fā)送一個(gè)請(qǐng)求,系統(tǒng) A 收到請(qǐng)求后氧骤,先查本地 ehcache 緩存呻疹,如果沒(méi)查到再查 redis。如果 ehcache 和 redis 都沒(méi)有筹陵,再查數(shù)據(jù)庫(kù)刽锤,將數(shù)據(jù)庫(kù)中的結(jié)果,寫(xiě)入 ehcache 和 redis 中朦佩。
限流組件并思,可以設(shè)置每秒的請(qǐng)求,有多少能通過(guò)組件语稠,剩余的未通過(guò)的請(qǐng)求宋彼,怎么辦?走降級(jí)颅筋!可以返回一些默認(rèn)的值宙暇,或者友情提示,或者空白的值议泵。
好處:
- 數(shù)據(jù)庫(kù)絕對(duì)不會(huì)死占贫,限流組件確保了每秒只有多少個(gè)請(qǐng)求能通過(guò)。
- 只要數(shù)據(jù)庫(kù)不死先口,就是說(shuō)型奥,對(duì)用戶(hù)來(lái)說(shuō),2/5 的請(qǐng)求都是可以被處理的碉京。
- 只要有 2/5 的請(qǐng)求可以被處理厢汹,就意味著你的系統(tǒng)沒(méi)死,對(duì)用戶(hù)來(lái)說(shuō)谐宙,可能就是點(diǎn)擊幾次刷不出來(lái)頁(yè)面烫葬,但是多點(diǎn)幾次,就可以刷出來(lái)一次凡蜻。
緩存穿透
對(duì)于系統(tǒng)A搭综,假設(shè)一秒 5000 個(gè)請(qǐng)求,結(jié)果其中 4000 個(gè)請(qǐng)求是黑客發(fā)出的惡意攻擊划栓。
黑客發(fā)出的那 4000 個(gè)攻擊兑巾,緩存中查不到,每次你去數(shù)據(jù)庫(kù)里查忠荞,也查不到蒋歌。
舉個(gè)栗子帅掘。數(shù)據(jù)庫(kù) id 是從 1 開(kāi)始的,結(jié)果黑客發(fā)過(guò)來(lái)的請(qǐng)求 id 全部都是負(fù)數(shù)堂油。這樣的話(huà)修档,緩存中不會(huì)有,請(qǐng)求每次都“視緩存于無(wú)物”称诗,直接查詢(xún)數(shù)據(jù)庫(kù)萍悴。這種惡意攻擊場(chǎng)景的緩存穿透就會(huì)直接把數(shù)據(jù)庫(kù)給打死。
解決方式很簡(jiǎn)單寓免,每次系統(tǒng) A 從數(shù)據(jù)庫(kù)中只要沒(méi)查到,就寫(xiě)一個(gè)空值到緩存里去计维,比如 set -999 UNKNOWN
袜香。然后設(shè)置一個(gè)過(guò)期時(shí)間,這樣的話(huà)鲫惶,下次有相同的 key 來(lái)訪(fǎng)問(wèn)的時(shí)候蜈首,在緩存失效之前,都可以直接從緩存中取數(shù)據(jù)欠母。
緩存擊穿
緩存擊穿欢策,就是說(shuō)某個(gè) key 非常熱點(diǎn),訪(fǎng)問(wèn)非常頻繁赏淌,處于集中式高并發(fā)訪(fǎng)問(wèn)的情況踩寇,當(dāng)這個(gè) key 在失效的瞬間,大量的請(qǐng)求就擊穿了緩存六水,直接請(qǐng)求數(shù)據(jù)庫(kù)俺孙,就像是在一道屏障上鑿開(kāi)了一個(gè)洞。
解決方式也很簡(jiǎn)單掷贾,可以將熱點(diǎn)數(shù)據(jù)設(shè)置為永遠(yuǎn)不過(guò)期睛榄;或者基于 redis or zookeeper 實(shí)現(xiàn)互斥鎖,等待第一個(gè)請(qǐng)求構(gòu)建完緩存之后想帅,再釋放鎖场靴,進(jìn)而其它請(qǐng)求才能通過(guò)該 key 訪(fǎng)問(wèn)數(shù)據(jù)。