緩存雪崩
對于系統(tǒng) A谐算,假設(shè)每天高峰期每秒 5000 個請求,本來緩存在高峰期可以扛住每秒 4000 個請求归露,但是緩存機(jī)器意外發(fā)生了全盤宕機(jī)洲脂。緩存掛了,此時 1 秒 5000 個請求全部落數(shù)據(jù)庫剧包,數(shù)據(jù)庫必然扛不住恐锦,它會報一下警,然后就掛了疆液。此時一铅,如果沒有采用什么特別的方案來處理這個故障,DBA 很著急堕油,重啟數(shù)據(jù)庫潘飘,但是數(shù)據(jù)庫立馬又被新的流量給打死了。
這就是緩存雪崩掉缺。
大約在 3 年前卜录,國內(nèi)比較知名的一個互聯(lián)網(wǎng)公司,曾因?yàn)榫彺媸鹿逝嗜Γ瑢?dǎo)致雪崩暴凑,后臺系統(tǒng)全部崩潰峦甩,事故從當(dāng)天下午持續(xù)到晚上凌晨 3~4 點(diǎn)赘来,公司損失了幾千萬。
緩存雪崩的事前事中事后的解決方案如下凯傲。
- 事前:redis 高可用犬辰,主從+哨兵,redis cluster冰单,避免全盤崩潰幌缝。
- 事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死诫欠。
- 事后:redis 持久化涵卵,一旦重啟,自動從磁盤上加載數(shù)據(jù)荒叼,快速恢復(fù)緩存數(shù)據(jù)轿偎。
用戶發(fā)送一個請求,系統(tǒng) A 收到請求后被廓,先查本地 ehcache 緩存坏晦,如果沒查到再查 redis。如果 ehcache 和 redis 都沒有,再查數(shù)據(jù)庫昆婿,將數(shù)據(jù)庫中的結(jié)果球碉,寫入 ehcache 和 redis 中。
限流組件仓蛆,可以設(shè)置每秒的請求睁冬,有多少能通過組件,剩余的未通過的請求看疙,怎么辦痴突?走降級!可以返回一些默認(rèn)的值狼荞,或者友情提示辽装,或者空白的值。
好處:
- 數(shù)據(jù)庫絕對不會死相味,限流組件確保了每秒只有多少個請求能通過拾积。
- 只要數(shù)據(jù)庫不死,就是說丰涉,對用戶來說拓巧,2/5 的請求都是可以被處理的。
- 只要有 2/5 的請求可以被處理一死,就意味著你的系統(tǒng)沒死肛度,對用戶來說,可能就是點(diǎn)擊幾次刷不出來頁面投慈,但是多點(diǎn)幾次承耿,就可以刷出來一次。
緩存穿透
對于系統(tǒng)A伪煤,假設(shè)一秒 5000 個請求加袋,結(jié)果其中 4000 個請求是黑客發(fā)出的惡意攻擊。
黑客發(fā)出的那 4000 個攻擊抱既,緩存中查不到职烧,每次你去數(shù)據(jù)庫里查,也查不到防泵。
舉個栗子蚀之。數(shù)據(jù)庫 id 是從 1 開始的,結(jié)果黑客發(fā)過來的請求 id 全部都是負(fù)數(shù)捷泞。這樣的話足删,緩存中不會有,請求每次都“視緩存于無物”肚邢,直接查詢數(shù)據(jù)庫壹堰。這種惡意攻擊場景的緩存穿透就會直接把數(shù)據(jù)庫給打死拭卿。
解決方式很簡單,每次系統(tǒng) A 從數(shù)據(jù)庫中只要沒查到贱纠,就寫一個空值到緩存里去峻厚,比如 set -999 UNKNOWN
。然后設(shè)置一個過期時間谆焊,這樣的話惠桃,下次有相同的 key 來訪問的時候,在緩存失效之前辖试,都可以直接從緩存中取數(shù)據(jù)辜王。
緩存擊穿
緩存擊穿,就是說某個 key 非常熱點(diǎn)罐孝,訪問非常頻繁呐馆,處于集中式高并發(fā)訪問的情況,當(dāng)這個 key 在失效的瞬間莲兢,大量的請求就擊穿了緩存汹来,直接請求數(shù)據(jù)庫,就像是在一道屏障上鑿開了一個洞改艇。
解決方式也很簡單收班,可以將熱點(diǎn)數(shù)據(jù)設(shè)置為永遠(yuǎn)不過期;或者基于 redis or zookeeper 實(shí)現(xiàn)互斥鎖谒兄,等待第一個請求構(gòu)建完緩存之后摔桦,再釋放鎖,進(jìn)而其它請求才能通過該 key 訪問數(shù)據(jù)承疲。