提到Redis我相信各位在面試,或者實際開發(fā)過程中對緩存雪崩纲堵,穿透,擊穿也不陌生吧闰渔,就算沒遇到過但是你肯定聽過席函,那三者到底有什么區(qū)別,我們又應(yīng)該怎么去防止這樣的情況發(fā)生呢冈涧,我們有請下一位受害者茂附。
一個大腹便便正蛙,穿著格子襯衣的中年男子,拿著一個滿是劃痕的mac向你走來营曼,看著快禿頂?shù)念^發(fā)乒验,心想著肯定是尼瑪頂級架構(gòu)師吧!但是我們腹有詩書氣自華蒂阱,虛都不虛锻全。
小伙子我看你的簡歷上寫到了Redis,那么我們直接開門見山录煤,直接懟常見的幾個大問題鳄厌,Redis雪崩了解么?
帥氣迷人的面試官您好妈踊,我了解的了嚎,目前電商首頁以及熱點數(shù)據(jù)都會去做緩存 ,一般緩存都是定時任務(wù)去刷新廊营,或者是查不到之后去更新的新思,定時任務(wù)刷新就有一個問題。
舉個簡單的例子:
如果所有首頁的Key失效時間都是12小時赘风,中午12點刷新的夹囚,我零點有個秒殺活動大量用戶涌入,假設(shè)當(dāng)時每秒 6000 個請求邀窃,本來緩存在可以扛住每秒 5000 個請求荸哟,但是緩存當(dāng)時所有的Key都失效了。此時 1 秒 6000 個請求全部落數(shù)據(jù)庫瞬捕,數(shù)據(jù)庫必然扛不住鞍历,它會報一下警,真實情況可能DBA都沒反應(yīng)過來就直接掛了肪虎。此時劣砍,如果沒用什么特別的方案來處理這個故障,DBA 很著急扇救,重啟數(shù)據(jù)庫刑枝,但是數(shù)據(jù)庫立馬又被新的流量給打死了。這就是我理解的緩存雪崩迅腔。
我刻意看了下我做過的項目装畅,感覺再吊的都不允許這么大的QPS直接打DB去,不過沒慢SQL加上分庫沧烈,大表分表可能還還算能頂掠兄,但是跟用了Redis的差距還是很大。
同一時間大面積失效,那一瞬間Redis跟沒有一樣蚂夕,那這個數(shù)量級別的請求直接打到數(shù)據(jù)庫幾乎是災(zāi)難性的迅诬。
你想想如果打掛的是一個用戶服務(wù)的庫,那其他依賴他的庫所有的接口幾乎都會報錯婿牍,如果沒做熔斷等策略基本上就是瞬間掛一片的節(jié)奏侈贷,你怎么重啟用戶都會把你打掛,等你能重啟的時候牍汹,用戶早就睡覺去了铐维,并且對你的產(chǎn)品失去了信心,什么垃圾產(chǎn)品慎菲。
面試官摸了摸自己的頭發(fā)嫁蛇,嗯,還不錯露该,那這種情況咋整睬棚?你都是怎么去應(yīng)對的?
處理緩存雪崩簡單解幼,在批量往Redis存數(shù)據(jù)的時候抑党,把每個Key的失效時間都加個隨機(jī)值就好了,這樣可以保證數(shù)據(jù)不會在同一時間大面積失效撵摆,我相信底靠,Redis這點流量還是頂?shù)米〉摹?/p>
setRedis(Key,value特铝,time + Math.random() * 10000)暑中;
如果Redis是集群部署,將熱點數(shù)據(jù)均勻分布在不同的Redis庫中也能避免全部失效的問題鲫剿,不過本渣我在生產(chǎn)環(huán)境中操作集群的時候鳄逾,單個服務(wù)都是對應(yīng)的單個Redis分片,是為了方便數(shù)據(jù)的管理灵莲,但是也同樣有了可能會失效這樣的弊端雕凹,失效時間隨機(jī)是個好策略。
或者設(shè)置熱點數(shù)據(jù)永遠(yuǎn)不過期政冻,有更新操作就更新緩存就好了(比如運維更新了首頁商品枚抵,那你刷下緩存就完事了,不要設(shè)置過期時間)赠幕,電商首頁的數(shù)據(jù)也可以用這個操作俄精,保險。
那你了解緩存穿透和擊穿么榕堰,可以說說他們跟雪崩的區(qū)別么?
嗯,了解逆屡,我先說一下緩存穿透吧圾旨,緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),而用戶不斷發(fā)起請求魏蔗,我們數(shù)據(jù)庫的 id 都是1開始自增上去的砍的,如發(fā)起為id值為 -1 的數(shù)據(jù)或 id 為特別大不存在的數(shù)據(jù)。這時的用戶很可能是攻擊者莺治,攻擊會導(dǎo)致數(shù)據(jù)庫壓力過大廓鞠,嚴(yán)重會擊垮數(shù)據(jù)庫。
小點的單機(jī)系統(tǒng)谣旁,基本上用postman就能搞死床佳,比如我自己買的阿里服務(wù)
像這種你如果不對參數(shù)做校驗,數(shù)據(jù)庫id都是大于0的榄审,我一直用小于0的參數(shù)去請求你砌们,每次都能繞開Redis直接打到數(shù)據(jù)庫,數(shù)據(jù)庫也查不到搁进,每次都這樣浪感,并發(fā)高點就容易崩掉了。
至于緩存擊穿嘛饼问,這個跟緩存雪崩有點像影兽,但是又有一點不一樣。
緩存雪崩是因為大面積的緩存失效莱革,打崩了DB峻堰,而緩存擊穿不同的是緩存擊穿是指一個Key非常熱點,在不停的扛著大并發(fā)驮吱,大并發(fā)集中對這一個點進(jìn)行訪問茧妒,當(dāng)這個Key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存左冬,直接請求數(shù)據(jù)庫桐筏,就像在一個完好無損的桶上鑿開了一個洞。
面試官露出欣慰的眼光拇砰,那他們分別怎么解決梅忌?
緩存穿透我會在接口層增加校驗,比如用戶鑒權(quán)校驗除破,參數(shù)做校驗牧氮,不合法的參數(shù)直接代碼Return,比如:id 做基礎(chǔ)校驗瑰枫,id <=0的直接攔截等踱葛。
這里我想提的一點就是丹莲,我們在開發(fā)程序的時候都要有一顆“不信任”的心,就是不要相信任何調(diào)用方尸诽。比如你提供了API接口出去甥材,你有這幾個參數(shù),那我覺得作為被調(diào)用方性含,任何可能的參數(shù)情況都應(yīng)該被考慮到洲赵,做校驗,因為你不相信調(diào)用你的人商蕴,你不知道他會傳什么參數(shù)給你叠萍。
舉個簡單的例子,你這個接口是分頁查詢的绪商,但是你沒對分頁參數(shù)的大小做限制苛谷,調(diào)用的人萬一一口氣查 Integer.MAX_VALUE 一次請求就要你幾秒,多幾個并發(fā)你不就掛了么部宿?是公司同事調(diào)用還好大不了發(fā)現(xiàn)了改掉抄腔,但是如果是黑客或者競爭對手呢?在你雙十一當(dāng)天就調(diào)你這個接口會發(fā)生什么理张,就不用我說了吧赫蛇。這是之前的Leader跟我說的,我覺得大家也都應(yīng)該了解下雾叭。
從緩存取不到的數(shù)據(jù)悟耘,在數(shù)據(jù)庫中也沒有取到,這時也可以將對應(yīng)Key的Value對寫為null织狐、位置錯誤暂幼、稍后重試這樣的值具體取啥問產(chǎn)品,或者看具體的場景移迫,緩存有效時間可以設(shè)置短點旺嬉,如30秒(設(shè)置太長會導(dǎo)致正常情況也沒法使用)。
這樣可以防止攻擊用戶反復(fù)用同一個id暴力攻擊厨埋,但是我們要知道正常用戶是不會在單秒內(nèi)發(fā)起這么多次請求的邪媳,那網(wǎng)關(guān)層Nginx本渣我也記得有配置項,可以讓運維大大對單個IP每秒訪問次數(shù)超出閾值的IP都拉黑荡陷。
那你還有別的辦法么雨效?
還有我記得Redis還有一個高級用法布隆過濾器(Bloom Filter)這個也能很好的防止緩存穿透的發(fā)生,他的原理也很簡單就是利用高效的數(shù)據(jù)結(jié)構(gòu)和算法快速判斷出你這個Key是否在數(shù)據(jù)庫中存在废赞,不存在你return就好了徽龟,存在你就去查了DB刷新KV再return。
那又有小伙伴說了如果黑客有很多個IP同時發(fā)起攻擊呢唉地?這點我一直也不是很想得通据悔,但是一般級別的黑客沒這么多肉雞传透,再者正常級別的Redis集群都能抗住這種級別的訪問的,小公司我想他們不會感興趣的屠尊。把系統(tǒng)的高可用做好了旷祸,集群還是很能頂?shù)摹?/p>
緩存擊穿的話耕拷,設(shè)置熱點數(shù)據(jù)永遠(yuǎn)不過期讼昆。或者加上互斥鎖就能搞定了作為暖男骚烧,代碼我肯定幫你們準(zhǔn)備好了浸赫。
總結(jié)
本文簡單的介紹了,Redis的雪崩赃绊,擊穿既峡,穿透,三者其實都差不多碧查,但是又有一些區(qū)別运敢,在面試中其實這是問到緩存必問的,大家不要把三者搞混了忠售,因為緩存雪崩传惠、穿透和擊穿,是緩存最大的問題稻扬,要么不出現(xiàn)卦方,一旦出現(xiàn)就是致命性的問題,所以面試官一定會問你泰佳。
大家一定要理解是怎么發(fā)生的盼砍,以及是怎么去避免的,發(fā)生之后又怎么去搶救逝她,你可以不是知道很深入浇坐,但是你不能一點都不去想,面試有時候不一定是對知識面的拷問黔宛,或許是對你的態(tài)度的拷問近刘,如果你思路清晰,然后知其然還知其所以然那就很贊宁昭,還知道怎么預(yù)防那來上班吧跌宛。
最后我繼續(xù)給你們做個小的技術(shù)總結(jié):
一般避免以上情況發(fā)生我們從三個時間段去分析下:
事前:Redis 高可用,主從+哨兵积仗,Redis cluster疆拘,避免全盤崩潰。
事中:本地 ehcache 緩存 + Hystrix 限流+降級寂曹,避免MySQL 被打死哎迄。
事后:Redis 持久化 RDB+AOF回右,一旦重啟,自動從磁盤上加載數(shù)據(jù)漱挚,快速恢復(fù)緩存數(shù)據(jù)翔烁。
上面的幾點我會在吊打系列Redis篇全部講一下這個月應(yīng)該可以吧Redis更完,限流組件旨涝,可以設(shè)置每秒的請求蹬屹,有多少能通過組件,剩余的未通過的請求白华,怎么辦慨默?走降級!可以返回一些默認(rèn)的值弧腥,或者友情提示厦取,或者空白的值。
好處:
數(shù)據(jù)庫絕對不會死管搪,限流組件確保了每秒只有多少個請求能通過虾攻。只要數(shù)據(jù)庫不死,就是說更鲁,對用戶來說霎箍,3/5 的請求都是可以被處理的。只要有 3/5 的請求可以被處理岁经,就意味著你的系統(tǒng)沒死朋沮,對用戶來說,可能就是點擊幾次刷不出來頁面缀壤,但是多點幾次樊拓,就可以刷出來一次。
這個在目前主流的互聯(lián)網(wǎng)大廠里面是最常見的塘慕,你是不是好奇筋夏,某明星爆出什么事情,你發(fā)現(xiàn)你去微博怎么刷都空白界面图呢,但是有的人又直接進(jìn)了条篷,你多刷幾次也出來了,現(xiàn)在知道了吧蛤织,那是做了降級赴叹,犧牲部分用戶的體驗換來服務(wù)的安全,可還行指蚜?
原文鏈接:
https://www.toutiao.com/i6807684838942835207/
文源網(wǎng)絡(luò)乞巧,僅供學(xué)習(xí)之用,如有侵權(quán)摊鸡,聯(lián)系刪除绽媒。為方便大家學(xué)習(xí)蚕冬,我還整理了一套學(xué)習(xí)資料,涵蓋Java虛擬機(jī)是辕、spring框架囤热、Java線程、數(shù)據(jù)結(jié)構(gòu)获三、設(shè)計模式等等旁蔼,免費提供給熱愛Java的同學(xué)~
關(guān)注公眾號【java圈子】,優(yōu)質(zhì)文章每日送達(dá)石窑。
file