目前的互聯(lián)網(wǎng)系統(tǒng)沒有幾個(gè)不使用緩存的, 但是只要使用緩存的話就會(huì)面臨這幾個(gè)問題, 如使用redis緩存技術(shù), 可能會(huì)遇到緩存的雪崩, 穿透, 以及擊穿.
首先來看一個(gè)簡單的正常緩存流程:
如用戶訪問JD, 然后JD去訪問redis, 如果redis有這個(gè)數(shù)據(jù)的話,就返回回去, 顯示出來,如果redis沒有數(shù)據(jù)的話,他就會(huì)去請求這個(gè)數(shù)據(jù)庫, 假如數(shù)據(jù)庫查到這個(gè)數(shù)據(jù)之后, 數(shù)據(jù)庫就會(huì)把這個(gè)結(jié)果同步到redis里面去, 同時(shí)它會(huì)把這個(gè)查詢到的結(jié)果返回回去.
基于上面的流程,我們來看一下什么是redis的緩存雪崩, 穿透, 擊穿?
聲明: 以下例子純屬虛構(gòu), 為便于理解所編.
1.redis的緩存雪崩
舉個(gè)例子, 在JD618的時(shí)候, 點(diǎn)進(jìn)去進(jìn)入到它的首頁, 這個(gè)首頁在618的時(shí)候訪問量是非常大的, 所以很多的數(shù)據(jù)是放到redis里面去緩存起來, 對(duì)應(yīng)redis的100key, 然后后臺(tái)人員設(shè)置的key的失效時(shí)間是三個(gè)小時(shí), 當(dāng)這個(gè)618期間, 購物車超過三小時(shí)之后, 這個(gè)首頁的redis緩存在一瞬間全部失效, 導(dǎo)致所有的請求都打到了這個(gè)數(shù)據(jù)庫上, 造成數(shù)據(jù)庫的響應(yīng)不及時(shí)掛掉, 這個(gè)時(shí)候, 首頁就沒辦法再繼續(xù)對(duì)外提供服務(wù). 這種現(xiàn)象就是緩存雪崩.
解決方案:
a.設(shè)置這個(gè)緩存的失效時(shí)間, 讓它不要在同一時(shí)間失效, 在我們設(shè)置這個(gè)緩存的時(shí)候, 隨機(jī)初始化這個(gè)失效時(shí)間, 這樣的話所有的緩存就不會(huì)在同一時(shí)間失效, 把所有的請求都打到數(shù)據(jù)庫上.
b.這個(gè)redis一般都是集群部署, 我們把這些熱點(diǎn)的key放到不同的節(jié)點(diǎn)上去, 讓這些熱點(diǎn)的緩存, 平均的分布在這個(gè)不同的redis節(jié)點(diǎn)上.
c.還有最暴力的方法就是不設(shè)置這個(gè)緩存失效的時(shí)間, 讓它永遠(yuǎn)不失效.
d.還有就是去跑這個(gè)定時(shí)任務(wù), 讓它去定時(shí)的刷這個(gè)緩存, 比如說我這個(gè)緩存設(shè)置了三小時(shí)時(shí)效, 那么我在失效之前, 就把這個(gè)redis緩存給他重新跑進(jìn)去, 然后再設(shè)置三小時(shí), 不斷的用這個(gè)定時(shí)任務(wù)去刷新這個(gè)緩存, 這個(gè)緩存就不會(huì)失效啊.
2.redis的緩存穿透
舉個(gè)例子, 比如某個(gè)網(wǎng)站非常的火爆, 動(dòng)了某些人的蛋糕, 然后遭到瘋狂的攻擊, 他的攻擊手段就是采用這個(gè)緩存穿透, 大家都知道數(shù)據(jù)庫主鍵從0開始遞增, 沒有負(fù)數(shù), 那么這個(gè)黑客就利用這一點(diǎn), 他不斷的利用這個(gè)id小于零的這個(gè)參數(shù)給我發(fā)請求, 我把數(shù)據(jù)庫里面,所有的數(shù)據(jù)都放到了redis緩存中去,但是他用id小于零的數(shù)來請求, redis里面并沒有這個(gè)id小于零的數(shù)據(jù), 這樣的話redis就查不到這個(gè)結(jié)果, 一旦這個(gè)redis 查不到這個(gè)結(jié)果, 就會(huì)去數(shù)據(jù)庫中去查, 造成這個(gè)請求不斷的打到這個(gè)數(shù)據(jù)庫上, 因?yàn)橹虚gredis這層不能攔截這樣的數(shù)據(jù), 這個(gè)redis直接被這種數(shù)據(jù)給穿透了直接穿透到數(shù)據(jù)庫里面. 這種現(xiàn)象就是緩存穿透. redis和數(shù)據(jù)庫中都沒有這樣的數(shù)據(jù), 一般出現(xiàn)這種情況, 都是一些不正常的用戶.
解決方案:
a.如果這個(gè)請求穿透了這個(gè)redis, 直接到這個(gè)數(shù)據(jù)庫中, 我數(shù)據(jù)庫無論查出什么結(jié)果, 是空的還是有值, 都會(huì)緩存到redis里去, 這樣他下次用同一個(gè)參數(shù)來發(fā)請求的時(shí)候, 就不會(huì)穿透這個(gè)redis.
b.但是他可能換不同的參數(shù), 這個(gè)解決方式就是把他這個(gè)ip拉黑.
c.但是他也可能換不同的ip, 然后第三個(gè), 就是對(duì)參數(shù)的合法性校驗(yàn), 在判斷這個(gè)參數(shù)不合法的時(shí)候, 直接return掉.
d.第四個(gè)方法就是使用布隆過濾器, 這是一個(gè)非常好的方式.
3.redis的緩存擊穿
舉個(gè)列子, 東哥在618的時(shí)候想搞一個(gè)噱頭, 把他自己珍藏多年的酒拿出來拍賣, 然后有非常多的人對(duì)這個(gè)酒非常的感興趣, 在9點(diǎn)的時(shí)候準(zhǔn)時(shí)拍賣這個(gè)鞋, 然后某個(gè)程序員就把酒的數(shù)據(jù)放到了redis緩存里, 對(duì)應(yīng)redis一個(gè)緩存的key, 拍賣的時(shí)候呢大家都非常的熱情, 一直拍賣了四小時(shí)還沒有結(jié)束這個(gè)拍賣, 但是這個(gè)酒對(duì)應(yīng)的緩存key, 他的失效時(shí)間是四個(gè)半小時(shí), 當(dāng)大家拍賣到四個(gè)半小時(shí)的時(shí)候, 這個(gè)酒的緩存key突然失效了, 導(dǎo)致大量的拍賣請求在redis里面查詢不到這個(gè)數(shù)據(jù), 這些請求就會(huì)直接打到這個(gè)數(shù)據(jù)庫,上面去, 造成這個(gè)數(shù)據(jù)庫響應(yīng)不及時(shí),掛掉. 這個(gè)案例呢就是redis的緩存擊穿.
注意:緩存擊穿是某一個(gè)熱點(diǎn)的key.
解決方式:
a.首先想到的是讓這個(gè)緩存永遠(yuǎn)不過期, 那這個(gè)方式肯定不太好.
b.使用分布式鎖, 如果是單體應(yīng)用的話, 就可以使用這個(gè)互斥鎖.
原理: 首先大量的用戶去訪問這個(gè)redis的請求數(shù)據(jù), 如果有的話就會(huì)返回給用戶, 如果redis里面這個(gè)數(shù)據(jù)為空的話,就會(huì)請求這個(gè)數(shù)據(jù)庫請求數(shù)據(jù), 我們就在這個(gè)請求數(shù)據(jù)庫這一步, 給他上上鎖, 那么這個(gè)時(shí)候就只有一個(gè)線程, 能搶到這個(gè)鎖, 所以也就只有一個(gè)線程能操作這個(gè)數(shù)據(jù)庫, 那么這個(gè)時(shí)候?qū)?shù)據(jù)庫的壓力就非常小, 當(dāng)他查詢到這個(gè)數(shù)據(jù)之后呢, 再把這個(gè)緩存重新寫到這個(gè)redis里面去, 其他沒有搶到鎖的線程, 讓它先睡幾毫秒, 然后再重新去redis里面去查詢這個(gè)數(shù)據(jù),因?yàn)槲覀冇幸粋€(gè)線程搶到了這個(gè)鎖, 把這個(gè)數(shù)據(jù)庫里面查詢出來的數(shù)據(jù)放到了redis里面去, 那么其他線程在訪問redis的時(shí)候, 這個(gè)redis里面就有數(shù)據(jù)了, 他就不用再去數(shù)據(jù)庫里面查詢數(shù)據(jù), 他們也就不用再去競爭這個(gè)分布式鎖, 他們直接在redis這一步就返回了. 所以這個(gè)是解決緩存擊穿最好的一個(gè)辦法.?
覺得此文不錯(cuò)的,點(diǎn)贊轉(zhuǎn)發(fā),本人非常感謝!






