《如何與面試官處朋友》系列-緩存擊穿、穿透、雪崩場(chǎng)景原理大調(diào)解
前面我們提到分布式多級(jí)緩存架構(gòu)的全貌,但總感覺(jué)少了些什么東西。在這樣大的場(chǎng)景下面,如果遇到緩存使用問(wèn)題那可咋辦?但自古英雄出少年,相信此刻你已踏馬西去,正走在尋找答案上得夕陽(yáng)西下。每每面談Redis大家肯定不陌生,反正就是各種被社會(huì)得毒打。上來(lái)就緩存問(wèn)題(擊穿、穿透、雪崩)三板斧,直接就是開(kāi)門紅,險(xiǎn)些讓我們招架不住。在這里我又日思夜想:
- 它們之間是如何造成的?
- 項(xiàng)目業(yè)務(wù)開(kāi)發(fā)中應(yīng)該注意些什么才能防范它們?
前言
說(shuō)時(shí)遲那時(shí)快,對(duì)面依然不存在。正在低頭玩手機(jī)的我,頓時(shí)感覺(jué)臉旁一陣涼風(fēng)襲來(lái),不由自主得抬起頭來(lái)。只見(jiàn)一位光頭大叔坐我對(duì)面,椅子頓時(shí)咯吱咯吱作響,順手還摸了一把那锃亮锃亮的頭。這雷厲風(fēng)行、英姿颯爽的身段,外加上處處逼人的寒冷氣息,猶如那劍指鋒芒,讓人背后感到針針發(fā)涼。
這難道是把傳說(shuō)中的敏捷開(kāi)發(fā)修煉到高層時(shí),所散發(fā)出的內(nèi)力嗎?猶如走出那六親不認(rèn)的步伐,走路帶風(fēng)
緩存業(yè)務(wù)的高效
面試官: 看你簡(jiǎn)歷上寫了很多緩存的內(nèi)容,那先談?wù)勀銓?duì)緩存的理解?為啥用它?
吒吒輝: 面試官您好,說(shuō)到緩存,那你算是找對(duì)人了,我能給你干到天亮,目前幾乎所有網(wǎng)站都會(huì)用它,我也是略知一二得。
其一
緩存一般指nosql,即非關(guān)系型數(shù)據(jù)庫(kù),它存儲(chǔ)的數(shù)據(jù)并不像數(shù)據(jù)庫(kù)那樣有很強(qiáng)的邏輯關(guān)系,類似干拿錢-->辦事-->走人的感覺(jué),以K--->V形式來(lái)操作。
因無(wú)邏輯關(guān)系,所以操作數(shù)據(jù)時(shí)就能避免很多費(fèi)時(shí)的邏輯數(shù)據(jù)計(jì)算操作,從而快速的獲取到數(shù)據(jù)。
但Redis的K-V其實(shí)隱含了兩層意思,不清楚你知道否?
因?yàn)镵-V本身具備Hash的特征,所以就分別為外Hash和內(nèi)Hash。
- 外Hash,指Redis的操作形式,即這種K-V的結(jié)果。從表現(xiàn)形式來(lái)的
- 內(nèi)Hash,為Redis的hash數(shù)據(jù)類型,從存儲(chǔ)的結(jié)構(gòu)來(lái)看。Redis的hash的結(jié)構(gòu)底層采用類似JAVA的HashMap。
面試官: 我記得那個(gè)Hash不是還有一個(gè)HashTable嗎?Redis咋不用它?
首先,它們底層都基于 Hashtable 來(lái)實(shí)現(xiàn)的,區(qū)別就在于Hashtable是線程安全的,而Hashmap則相反。
因?yàn)镽edis本身就是單線程,故它的模式就 決定它為線程安全。所以采用 Hashtable 這種支持多線程的線程安全機(jī)制就有點(diǎn)浪費(fèi)。好比如你一個(gè)人是想咋玩就咋玩。
Redis-Hash采用數(shù)組+鏈表的形式共同組成HASH。
Redis真的的單線程嗎?后面發(fā)文分析
其二
緩存多以內(nèi)存為存儲(chǔ)介質(zhì),遠(yuǎn)不是MySQL走磁盤能比擬的。 我直接給你拿京東的金士頓來(lái)做個(gè)比較,你老人家就明白了。
磁盤性能:
內(nèi)存性能:
內(nèi)存性能:2666MHz * 64bit(單通道,雙通道則128bit) / 8(位到字節(jié)單位轉(zhuǎn)換) = 21.328GB/s。這只是理論,實(shí)際發(fā)揮還要看內(nèi)存控制器,實(shí)際上2666單條跑出來(lái)的數(shù)據(jù)在18~20GB/s差不多了。
差距:21328Mb/500Mb(SSD)=40.656。那你機(jī)械硬盤就會(huì)有上百倍的差距。一般部署服務(wù)器的磁盤不可能都是固態(tài)。而是固+機(jī),簡(jiǎn)稱:古(固)巨基(機(jī))。也是出于成本的考慮,
面試官: 那固態(tài)和機(jī)械為啥差距這么大呢?
機(jī)械磁盤的讀寫是需要根據(jù)磁頭臂的移動(dòng)+磁頭讀寫數(shù)據(jù)才能完成,它是由外到內(nèi)的寫入每一個(gè)扇區(qū)。
而固態(tài)硬盤會(huì)有多個(gè)閃存。每個(gè)閃存可能有4、8、16個(gè)閃存顆粒構(gòu)成。讀寫時(shí),可以同時(shí)多個(gè)閃存采用電信號(hào)去到存儲(chǔ)位置中來(lái)進(jìn)行讀寫,相當(dāng)于并行的方式,所以比你磁盤轉(zhuǎn)動(dòng)得要快得多。
好比如傳遞口令的工作。前者是先跑到站點(diǎn),在寫下通知。然后才能繼續(xù)跑到下一個(gè)站點(diǎn)進(jìn)行通知。而后者則是直接通過(guò)電話口頭傳遞指令給多個(gè)站點(diǎn)
閃存是什么?
閃存是一種存儲(chǔ)介質(zhì),和內(nèi)存最大的區(qū)別是斷電后數(shù)據(jù)仍然不會(huì)丟失(和硬盤相似),數(shù)據(jù)刪除不是以單個(gè)的字節(jié)為單位而是以固定的區(qū)塊為單位,區(qū)塊大小一般為256KB到20MB。
吒吒輝: 所以你像MySQL這樣的東西在高并發(fā)請(qǐng)求下,那就很有問(wèn)題,我在給你老人家打個(gè)比方
MySQL-3個(gè)關(guān)聯(lián)表的數(shù)據(jù)結(jié)果,我給它安排到緩存中,在用內(nèi)存來(lái)提提速,這不就是省去了數(shù)據(jù)在數(shù)據(jù)庫(kù)中組裝、從磁盤中讀取的時(shí)間嗎?
所以,用它,將可以大范圍抵御高頻率的用戶訪問(wèn)請(qǐng)求,避免做重復(fù)而又復(fù)雜的計(jì)算工作。直接將其攔截到數(shù)據(jù)庫(kù)上游,保護(hù)后方的機(jī)器。讓你請(qǐng)求在高也得給我規(guī)規(guī)矩矩得。
你像拿電商首頁(yè)、秒殺系統(tǒng)、熱點(diǎn)推薦等數(shù)據(jù),那都要100%考慮緩存得,當(dāng)然業(yè)務(wù)需要提速的話,都是可以用得到的
一眼望去你的簡(jiǎn)歷,就 緩存擊穿 我心房
面試官: 嗯,不錯(cuò),那使用緩存常會(huì)遇到緩存的3大問(wèn)題,你先給說(shuō)下緩存擊穿是什么?
吒吒輝: 其實(shí),緩存使用問(wèn)題主要就集中在數(shù)據(jù)為臟數(shù)據(jù)、使用時(shí)緩存失效、緩存實(shí)例不可用等問(wèn)題上。
而我們一切的方案都折中于滿足于主要問(wèn)題。而這“緩存擊穿”就是緩存失效的情景。
什么是緩存擊穿呢?
緩存擊穿就是當(dāng)你請(qǐng)求訪問(wèn)緩存Key時(shí),恰巧它失效了。而這時(shí)數(shù)據(jù)還沒(méi)更新到緩存中,外加上高頻訪問(wèn)請(qǐng)求。相當(dāng)于把緩存都給打穿了。
從而讓這些透穿的請(qǐng)求分分鐘把數(shù)據(jù)庫(kù)給打得懷疑人生,甚至不能自理。猶如下面這感覺(jué),表面風(fēng)平浪靜,實(shí)則早以暗藏殺機(jī)
為什么有這么大的殺傷力?
因?yàn)槟阍瓉?lái)的請(qǐng)求走的是緩存,現(xiàn)在它沒(méi)了。所以數(shù)據(jù)庫(kù)就需要多承受幾十、上百倍的數(shù)據(jù)訪問(wèn)壓力。
假設(shè)當(dāng)時(shí) 10000/s 個(gè)請(qǐng)求,緩存能扛住9000/s 個(gè)請(qǐng)求,緩存一失效。此時(shí)10000 /s個(gè)請(qǐng)求全部落到數(shù)據(jù)庫(kù),那自然是招架不住。
這時(shí)一般數(shù)據(jù)庫(kù)的CPU會(huì)飆到100%,服務(wù)器會(huì)卡死??赡蹹BA會(huì)直接重啟一波。那不用多說(shuō),會(huì)直接再次陷入僵局。
當(dāng)然這種規(guī)模訪問(wèn),一般都有監(jiān)控系統(tǒng),在得出數(shù)據(jù)庫(kù)負(fù)載過(guò)大的情況時(shí),會(huì)發(fā)送報(bào)警的消息通知。然后再針對(duì)性想辦法。
面試官:那你覺(jué)得這種情況要怎么做?
吒吒輝: 解鈴還須系鈴人,既然是在使用過(guò)程中遇到突變情況,那么就需要在業(yè)務(wù)&緩存上來(lái)提前進(jìn)行搭橋。
一、業(yè)務(wù)層
1.1 加鎖
如果緩存失效沒(méi)拿到數(shù)據(jù),就先拿到互斥鎖然后去數(shù)據(jù)庫(kù)查詢數(shù)據(jù),然后在返回結(jié)果,并重新設(shè)置緩存。
這樣高頻的請(qǐng)求訪問(wèn)就會(huì)被限制為1個(gè)請(qǐng)求。數(shù)據(jù)庫(kù)的壓力自然也會(huì)變小,后面相同請(qǐng)求繼續(xù)走緩存。
public static getData(string $key) { //從緩存讀取數(shù)據(jù) $result = $redis->get($key); //緩存中不存在數(shù)據(jù) if ($result ==null ) { //獲取swoole互斥鎖 $lock = new Swoole\Lock(SWOOLE_MUTEX); if ($lock->lock()) { $result = getDataFormMysql($key); //獲取到數(shù)據(jù)庫(kù) if ($result != null)) setDataFromRedis($key, $result)); return $lock->unlock(); }else { //獲取鎖失敗,則重試 usleep(10000); $result = getData($key;) } } return $result; }
但這加鎖方案是不是會(huì)阻塞請(qǐng)求,影響用戶體驗(yàn)?zāi)兀?/span>
還是一個(gè)遞歸的調(diào)用。沒(méi)辦法,你沒(méi)搶到鎖的請(qǐng)求只能等待或重試。不然你難道要打人爆力搶鎖呀?。?!
有什么方式可以改進(jìn)?
用隊(duì)列。
其實(shí)上面的鎖還可改為最大重試的次數(shù),不然如果遇到網(wǎng)絡(luò)波動(dòng)而導(dǎo)致請(qǐng)求阻塞,這樣不斷的進(jìn)行重試獲取鎖,就會(huì)把機(jī)器給拖垮。當(dāng)然網(wǎng)站請(qǐng)求量少,基本上沒(méi)啥問(wèn)題。但萬(wàn)事還得小心。
1.2. 隊(duì)列
請(qǐng)求沒(méi)拿到緩存,那么把請(qǐng)求放入到一個(gè)去重的隊(duì)列中,相同查詢請(qǐng)求保留一個(gè)即可。這時(shí)就可以返回客戶端“您稍安勿躁,請(qǐng)等一下”,然后異步執(zhí)行隊(duì)列去獲取數(shù)據(jù)庫(kù)中的數(shù)據(jù)并更新到緩存中。后面來(lái)的請(qǐng)求就可讀緩存中的數(shù)據(jù)。
這種情況下 都是直接響應(yīng)用戶, 那這樣不是就有很多用戶無(wú)法拿到數(shù)據(jù)嗎?
如果你覺(jué)得這樣不好,可以來(lái)個(gè)折中的辦法,第一次的請(qǐng)求入隊(duì)列,讓它去讀取數(shù)據(jù)并更新緩存。第二次乃至后面的請(qǐng)求在入隊(duì)列時(shí)進(jìn)行判斷。如果發(fā)現(xiàn)會(huì)重復(fù),那么就等0.1s,然后在去緩存里面拿數(shù)據(jù)返回。
這樣就可以,不會(huì)一直等待數(shù)據(jù)。但等待是需要的。如果0.1s還不行,就返回提示內(nèi)容給客戶端,不讓其持續(xù)等待。還能夠省去爭(zhēng)取鎖的消耗。bingo
1.3. 無(wú)效時(shí)間
讓緩存無(wú)失效時(shí)間。但最好設(shè)置一個(gè)計(jì)劃任務(wù),不然緩存可能有臟數(shù)據(jù)的問(wèn)題,但更新不頻繁或數(shù)據(jù)不怎么發(fā)生變化的業(yè)務(wù)沒(méi)事
二、緩存層
緩存層面就是自主實(shí)現(xiàn)更新數(shù)據(jù)到緩存中,當(dāng)然這個(gè)肯定是需要結(jié)合后臺(tái)任務(wù)。比如緩存時(shí)間為10S。我反手一個(gè)定時(shí)任務(wù)就安排在9S,把數(shù)據(jù)更新到緩存中。或者考慮發(fā)布訂閱模式,監(jiān)聽(tīng)到頻道來(lái)進(jìn)行數(shù)據(jù)更新。
面試官:前面你提到加鎖,那如果是分布式多實(shí)例的場(chǎng)景你要咋辦?
你老說(shuō)得確實(shí)是個(gè)問(wèn)題,對(duì)應(yīng)一般高流量的網(wǎng)站都是會(huì)采用分布式多機(jī)器的架構(gòu)模式。這時(shí)候也會(huì)有并發(fā)的問(wèn)題產(chǎn)生。
因?yàn)楹?jiǎn)單的互斥鎖,只能滿足單機(jī)器上的擊穿問(wèn)題。如果在分布式的并發(fā)場(chǎng)景下,就變成同時(shí)有多臺(tái)機(jī)器拿到鎖而訪問(wèn)到后端。
這樣就達(dá)到了并行的訪問(wèn)形式,數(shù)據(jù)庫(kù)也會(huì)因?yàn)檎?qǐng)求量過(guò)大而造成并發(fā)訪問(wèn)。所以說(shuō)這種情況下還是有很大壓力。那要怎么解決呢?
咱們直接給他換成分布式鎖就可以達(dá)到效果,比如用:Redis、memcache、zookeeper等來(lái)做。
面試官:說(shuō)了這么多,你更傾向于那個(gè)呢?
吒吒輝:
我覺(jué)得還是要看業(yè)務(wù)場(chǎng)景,簡(jiǎn)單的肯定是鎖和設(shè)置不過(guò)期的數(shù)據(jù),對(duì)于后者可以通過(guò)訂閱頻道或定時(shí)任務(wù)來(lái)更新數(shù)據(jù),防止有臟數(shù)據(jù)。
如果要用戶體驗(yàn)好點(diǎn)的話,就搞隊(duì)列吧,這樣不用一直阻塞等待,在分布式的場(chǎng)景那分布式鎖還是得來(lái)獨(dú)自安排。
二眼回眸你的簡(jiǎn)歷,那 緩存穿透 我心臟
面試官: 那你在談?wù)劸彺娲┩赴桑?/span>
緩存穿透就是當(dāng)你請(qǐng)求訪問(wèn)緩存Key時(shí),緩存本身的內(nèi)容就沒(méi)有它,數(shù)據(jù)庫(kù)更無(wú)它的身影。這樣的請(qǐng)求就會(huì)穿透緩存直達(dá)數(shù)據(jù)庫(kù)。
如果這是一個(gè)高頻請(qǐng)求,數(shù)據(jù)庫(kù)就可能被惡意的打垮。就比如一種大網(wǎng),請(qǐng)求就通過(guò)中間的縫隙穿透過(guò)去。好像漏網(wǎng)之魚
為什么說(shuō)被惡意的打垮呢?
原因就在,數(shù)據(jù)庫(kù)本身就無(wú)這個(gè)key對(duì)應(yīng)的數(shù)據(jù),而緩存又依照數(shù)據(jù)庫(kù)生存,那它就更沒(méi)得了。
比如 拿商品id=-1的條件來(lái)查詢;你說(shuō)這不是惡意是啥?明明就沒(méi)有,結(jié)果還是要來(lái)查詢。明擺著給你穿小鞋。欲加之罪,何患無(wú)辭。
面試官: 那你面對(duì)這種情況會(huì)怎么來(lái)解決?咳咳,這個(gè)還需要大致分析下才有出路。
一、業(yè)務(wù)上:
1.1.訪問(wèn)限制黑名單
如果不想業(yè)務(wù)上加入太多不可控因子。那么可以在服務(wù)端加入訪問(wèn)限定黑名單機(jī)制。咋個(gè)意思?
用戶請(qǐng)求發(fā)送到服務(wù)端時(shí),服務(wù)端調(diào)用Redis-client先拿 EXISTS 判斷緩存數(shù)據(jù)是否存在,如果發(fā)現(xiàn)沒(méi)得值,就把當(dāng)前key記錄到訪問(wèn)限制黑名單列表,訪問(wèn)次數(shù)+1,并設(shè)置合適的過(guò)期時(shí)間。然后再去數(shù)據(jù)庫(kù)讀取數(shù)據(jù)。
如果訪問(wèn)的key是我們緩存中的內(nèi)容,那么自然可以去數(shù)據(jù)庫(kù)拿到數(shù)據(jù),如果同一個(gè)key或幾個(gè)key都一二再再而三拿不到數(shù)據(jù),那么就肯定有問(wèn)題。對(duì)應(yīng)黑名單限制列表的訪問(wèn)次數(shù)自然也就上去了。
這樣后面請(qǐng)求獲取key時(shí),就先去訪問(wèn)控制列表看看有沒(méi)得它(key)這個(gè)刺兒頭,有,我直接頭都不回的給你拒絕掉,還順手給你一大嘴巴子(給黑名單里面的key重置過(guò)期時(shí)間),這樣后面惡意請(qǐng)求在過(guò)來(lái),可以保證這個(gè)黑名單還有key的名字。直接讓它面壁思過(guò),給我唱征服。
1.2.設(shè)置Key為null
如果訪問(wèn)key的在數(shù)據(jù)庫(kù)中也沒(méi)有,就直接給給該key設(shè)置為NULL,并設(shè)置過(guò)期時(shí)間,畢竟null也需要占據(jù)空間的,還是得讓它自己刪除。這樣后面請(qǐng)惡意請(qǐng)求直接讓它與null談話
面試官: 如果現(xiàn)在惡意請(qǐng)求兵分三路,分多波攻擊,每次key都不一樣。會(huì)怎么樣呢?
如果惡意請(qǐng)求要打持久戰(zhàn),那流量肯定大,每次訪問(wèn)的都是不一樣。
那首先肯定會(huì)有限流方案來(lái)限制這種級(jí)別的流量,以減少后端服務(wù)的沖擊。所以到后端服務(wù)的流量整體規(guī)模就會(huì)變小,但總量可能還是會(huì)大于數(shù)據(jù)庫(kù)承受壓力。
如果請(qǐng)求key隨機(jī)性和流量規(guī)模很大,相當(dāng)于一個(gè)key訪問(wèn)一次就不來(lái)了,這樣存儲(chǔ)黑名單的列表就會(huì)很大。后面做查詢時(shí)也就比較慢了。
例如用Redis-zset存儲(chǔ),流量規(guī)模一旦過(guò)大,它就需要很多的存儲(chǔ)空間, 所以咱們可采用bitmap來(lái)減少存儲(chǔ)空間和把Key分散開(kāi)來(lái)。
如果請(qǐng)求key的隨機(jī)性不是很大,流量規(guī)模大。但是這種類型請(qǐng)求很多,這時(shí)候還得看業(yè)務(wù)情況。
如果是個(gè)性業(yè)務(wù),比如用戶訪問(wèn)個(gè)人信息,這時(shí)得把key放到黑名單限制列表中,限制一下它那120邁的速度。
如果是共性業(yè)務(wù),比如直接瀏覽的商品,那肯定得用隊(duì)列來(lái)降降速。不然數(shù)據(jù)庫(kù)真受不住。
1.3.第三方
那有沒(méi)有更簡(jiǎn)單的方案,感覺(jué)上面很是繁瑣? 其實(shí),也有,你選擇“布隆過(guò)濾器”這家伙就可以。
它是第Redis里面的第三方模塊,使用時(shí)你自己需要做編譯安裝才可使用,不像Redis自帶的常用數(shù)據(jù)類型。
每次訪問(wèn)緩存時(shí),直接用布隆過(guò)濾器里面進(jìn)行判斷是否存在key,如果有,就去Redis里面讀取,無(wú),就拒絕請(qǐng)求。
相當(dāng)于過(guò)濾請(qǐng)求的作用,而且布隆過(guò)濾器存儲(chǔ)數(shù)據(jù)的單位是位(bit),所以整體占據(jù)的存儲(chǔ)容量也不是很大,重要的是可以做到攔截請(qǐng)求操作。
但布隆過(guò)濾器自身具備不高誤判率,隨著存儲(chǔ)的數(shù)量增多也會(huì)加大。所以布隆過(guò)濾器如何說(shuō)找到了緩存那么是可能沒(méi)有,如果找不到這個(gè)緩存那么就是一定沒(méi)有緩存。
但這對(duì)廣大的請(qǐng)求來(lái)說(shuō),幾乎影響不大。
你這里是說(shuō)請(qǐng)求訪問(wèn)key時(shí)就用布隆過(guò)濾器來(lái)判斷,那剛開(kāi)始沒(méi)存儲(chǔ)數(shù)據(jù)咋辦,干看著它?
**不,不,不,你得這么來(lái)看 **
啟動(dòng)系統(tǒng)時(shí),直接做緩存預(yù)熱。就提前把熱點(diǎn)數(shù)據(jù)或當(dāng)前業(yè)務(wù)需要的緩存數(shù)據(jù)給載入到緩存中。這樣后面請(qǐng)求來(lái)了,我發(fā)現(xiàn)你不是我自家兄弟的Key,直接妥妥拒絕沒(méi)得商量。
可這熱點(diǎn)數(shù)據(jù)、業(yè)務(wù)需要的數(shù)據(jù)要怎么發(fā)現(xiàn)呢?網(wǎng)站上線一開(kāi)始肯定沒(méi)運(yùn)行啊,讓布隆過(guò)濾器喝西北風(fēng)?
。。。。。。我這個(gè)。
熱點(diǎn)數(shù)據(jù)肯定是網(wǎng)站跑起來(lái)經(jīng)過(guò)運(yùn)行一段時(shí)間才能確定的,一般Redis不是會(huì)做持久化嘛,不然Redis-(RDB、AOF)吃干飯呀。直接根據(jù)持久化的數(shù)據(jù)恢復(fù)業(yè)務(wù)中需要的熱點(diǎn)數(shù)據(jù),一并更新到布隆過(guò)濾器。
雖然新系統(tǒng)、新業(yè)務(wù)沒(méi)有遺留的歷史數(shù)據(jù),那我們可設(shè)置些規(guī)則,例如:最新、點(diǎn)擊量高的數(shù)據(jù)給篩它一波。然后把滿足這些規(guī)則的數(shù)據(jù)給設(shè)置到緩存和布隆過(guò)濾器中。
二、緩存上
這個(gè)比較直接,就是根據(jù)緩存的命中率來(lái)進(jìn)行分析,如果我發(fā)現(xiàn)Redis內(nèi)部某個(gè)key一直是未命中,而且訪問(wèn)次數(shù)還跳躍的很。那就直接上報(bào)給key的黑名單。
這樣后面如果請(qǐng)求訪問(wèn)的key只要為它就直接干掉,相當(dāng)于做一個(gè)黑key的發(fā)現(xiàn),然后再來(lái)對(duì)它來(lái)做攔截
三眼再看你的簡(jiǎn)歷,那 緩存雪崩 讓我徹底淪陷
面試官: 那緩存雪崩要怎么解決?
吒吒輝: 緩存雪崩其實(shí)和緩存擊穿有點(diǎn)類似,都是緩存失效。只不過(guò)它們各自針對(duì)緩存失效的情況不一樣。
緩存雪崩是緩存同時(shí)大面積失效而導(dǎo)致請(qǐng)求像雪崩式的一擁而下,直搗黃龍(數(shù)據(jù)庫(kù))。而緩存擊穿針對(duì)的是單個(gè)key訪問(wèn)恰巧失效的問(wèn)題。
由此可見(jiàn)緩存雪崩的破壞力更大。關(guān)鍵要點(diǎn)在于同時(shí)會(huì)有大面積的key失效。那怎么解決呢?
一、業(yè)務(wù)上
設(shè)置緩存時(shí),采用隨機(jī)時(shí)間來(lái)把每個(gè)key的過(guò)期時(shí)刻都打亂,這樣就不會(huì)統(tǒng)一集中失效,造成緩存雪崩。
是不是只要緩存大面積失效就會(huì)對(duì)后方造成問(wèn)題?
這倒還不一定,如果你當(dāng)前的子系統(tǒng),不是什么流量大的業(yè)務(wù),你會(huì)有問(wèn)題嗎?
比如:個(gè)人中心的基本資料,誰(shuí)沒(méi)事天天看,又不是不認(rèn)識(shí)自己。
所以這問(wèn)題還是得看業(yè)務(wù)特點(diǎn),如果本身流量就比較高的業(yè)務(wù)在設(shè)計(jì)緩存時(shí),就不要把緩存設(shè)置的那么集中。
比如:秒殺、熱榜、首頁(yè)的聚合數(shù)據(jù)等。這時(shí)就應(yīng)該把它們的過(guò)期時(shí)間給錯(cuò)開(kāi)掉
二、模式上
大流量的業(yè)務(wù)下,自然要緩存的數(shù)據(jù)也會(huì)很多。所以在設(shè)置緩存時(shí),也可采用緩存數(shù)據(jù)分片來(lái)把緩存分配到不同的實(shí)例上來(lái)進(jìn)行緩存,這樣每個(gè)緩存實(shí)例的壓力也會(huì)小很多。這樣子就算失效了一部分它敢給我集體罷工嘛?
如果要保證多個(gè)緩存實(shí)例宕機(jī)后,整個(gè)系統(tǒng)還想繼續(xù)麻溜的跑起來(lái),那集群的方案自然是少不了的,有它才可以通過(guò)備節(jié)點(diǎn)快速補(bǔ)充上來(lái)。從而恢復(fù)業(yè)務(wù)的正常運(yùn)行,抵御那些過(guò)高的網(wǎng)絡(luò)請(qǐng)求。
總結(jié)
- 緩存高效于模式上的提速,內(nèi)外HASH你可知道?
- 緩存穿透是請(qǐng)求訪問(wèn)數(shù)據(jù)庫(kù)本不該出現(xiàn)的key,算惡意請(qǐng)求
- 業(yè)務(wù)上的規(guī)則檢查防護(hù)
- 布隆過(guò)濾器
- 緩存設(shè)null
- 緩存擊穿是請(qǐng)求訪問(wèn)key時(shí)恰巧失效,而打到數(shù)據(jù)庫(kù)。
- 互斥鎖、隊(duì)列、分布式鎖
- 緩存無(wú)失效
- 緩存雪崩是緩存出現(xiàn)大面積失效,而導(dǎo)致流量洪峰流到數(shù)據(jù)庫(kù)
- 隨機(jī)失效
- 集群處理
- 數(shù)據(jù)分片拆分
思考題
- 緩存三大問(wèn)題的本質(zhì)是什么造成的?
- 設(shè)計(jì)的方案很多,具體實(shí)踐拿捏看什么?
- 你網(wǎng)站的熱點(diǎn)數(shù)據(jù)發(fā)現(xiàn)規(guī)則會(huì)怎么考慮呢?要不要單獨(dú)出業(yè)務(wù)?
如有感悟,歡迎關(guān)注。
點(diǎn)贊、分享、留言都可走一走。本來(lái)文章還是可以寫很多東西,不過(guò)我感覺(jué)像微服務(wù)、優(yōu)化、分布式等場(chǎng)景在這里不大合適,就準(zhǔn)備單獨(dú)出技術(shù)知識(shí)分享專題來(lái)做,不然大家就不好理解了。
也可以后面來(lái)一個(gè)高級(jí)版的緩存問(wèn)題防御。這樣大家可能很好理解。具體怎么安排大家可留言告知。除此之外以下相關(guān)的內(nèi)容幫助大家提升
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!