國慶加中秋過去了,你們準備好學習了麼?web
redis 在項目中用的話,主要就是用做緩存了redis
既然用做緩存,那就確定會有 緩存穿透/緩存擊穿/緩存雪崩 的問題數據庫
這篇文章就來講說,遇到這種狀況時,該如何去處理數組
緩存穿透
首先我們搞明白什麼是緩存穿透?這三個詞這麼像,得把概念搞清楚不是緩存
其實只是從字面意思上來看的話,大概也能知道一點兒,緩存穿透嘛,就是直接穿過了緩存,將請求打到了數據庫上面去服務器
通常狀況下,去查詢數據的話,緩存裏面應該都是有的,可是防不住黑客呀,若是黑客請求查詢的是數據庫裏面根本不存在的數據,數據庫裏面都沒有的數據,緩存裏面確定也不會有了,對吧,那麼此時請求就會打到我們的數據庫裏面去,這就是緩存穿透微信
你想啊,黑客想要攻擊的話,怎麼可能只請求一次呢,確定是大量的請求過來,由於是拿數據庫裏面不存在的 id 來請求的,那麼這些請求毫無疑問直接打到了數據庫上面去,那我們的數據庫可能就會由於這些大量的請求直接宕掉數據結構
如何解決呢?併發
我們回到產生這個問題的場景中,爲何大量的請求會打到數據庫上面來?由於緩存裏面沒有對應的 key 對吧,因此纔會越過緩存直接到數據庫app
那麼問題就好解決了嘛,緩存裏面沒有對應的 key ?OK ,若是這個 key 數據庫裏面也沒有,那我就在 redis 裏面,存上這個 key ,值是 null ,這樣若是有查詢這個 key 的請求,我直接返回 null 就完事兒了,也就不用打到數據庫上面去了
注意一下,要記得設置它的過時時間,通常三到五分鐘就夠了
可是對方是個黑客呀,可能就用一個 key 去請求麼?他可能會在短期內用大量的 key 來發送請求,那若是一個 key 就在 redis 中存儲一個 null 值的話,那麼多 key 是否是就會存儲那麼多個 null 值嘞?
這樣的話, redis 裏面是否是都是值爲 null 的了?
因此有沒有更好的解決辦法呢?
那必須得有!布隆過濾器,你值得嘗試
什麼是布隆過濾器呢?就是它能告訴你,某個值必定不存在或者可能存在( emmmm ,也不知道我有沒有說清楚
因此能夠將數據庫的內容緩存一份到布隆過濾器,這樣的話,當大量的請求過來的時候, redis 裏面沒有,不要緊,再去布隆過濾器過濾一下,這樣請求不用打到數據庫上面去,就能肯定這個 key 數據庫中有沒有
這樣不就下降了數據庫的壓力麼,可真是個天才~
緩存擊穿
緩存擊穿說的是,在高併發狀況下,若是好多個請求都在查詢一個 key ,好巧不巧的是,這個 key 由於某些緣由失效了(好比設置的過時時間到了,緩存服務器宕機了),這樣就會致使那麼多的請求都直接打到數據庫上面去了
那若是這些請求的數量足夠大的話,可能直接把數據庫就幹掉了
知道了形成結果的緣由,那麼尋找解決方案也就好辦了
不是由於好多個請求打到了數據庫嘛,可是它們請求的都只是一個 key ,因此這裏可使用排斥鎖來實現,第一個請求達到請求 key 發現緩存裏面沒有,容許它去數據庫查詢,同時加鎖,這樣第二個請求,第三個請求…都會被鎖阻塞到當前,不會再打到數據庫,這樣就減小了數據庫的併發壓力
緩存雪崩
緩存雪崩,雪崩雪崩嘛,就比較嚴重,擊穿說的是一個 key 失效的狀況,雪崩指的是大規模的緩存失效狀況的發生,這是有可能發生的,好比說個人緩存服務器宕機了,那是否是直接就大規模的緩存失效了;或者說,我當時爲了圖省事,好多個 key 設置的過時時間都是同樣的,而後恰好在緩存都失效的時候,好多請求不一樣的 key 過來了
解決方案的話,其實就不適合使用加鎖的方式去解決了,由於這是好多請求不一樣的 key ,它不是一個嘛
並且嘞,我們是由於好多個 key 設置的過時時間都是同樣的,因此解決方案就是,我們不設置一樣的時間讓緩存失效了,我們給一個隨機時間,讓緩存隨機失效,這樣的話,大規模的緩存失效狀況就減小不少了
那還要一種狀況呢,就是若是個人緩存服務器直接宕機了,這怎麼辦?也好弄,來個集羣就解決了,這裏只是一個解決方案,它的落地實現不是本文重點哈~
再談 布隆過濾器
OK ,你若是看到這裏的話,其實這篇文章的內容就說完了
可是我感受布隆過濾器那塊,我沒有說清楚,因此在這裏拿出來詳細說一說(我知道你必定又在默默誇阿粉是個暖男了,乖,知道就行了,不要真說出來,我會害羞的
布隆過濾器是一種數據結構,它是一種機率型的數據結構,就是它能告訴你「某樣東西必定不存在或者可能存在」
你可能會說,這話剛剛不是說過了嘛,原本就挺拗口的,你咋還說
還不是由於這句話比較重要,我以爲把這句話理解透徹了,那麼對布隆過濾器理解的應該也就到位了
來,爲了形象生動一些,我們舉個例子~ 布隆過濾器是一個 bit 向量或者說 bit 數組,大概長這樣:
![](http://static.javashuo.com/static/loading.gif)
如今,咱們須要把 「AliPay」 這個字段給存儲進去 大概的存儲過程就是:將要映射的值,使用多個不一樣的哈希函數生成多個哈希值,而後每一個生成的哈希值指向的 bit 置爲 1
以給的爲例,咱們如今將 「AliPay」 這個值,經過三個不一樣的哈希函數進行映射,那麼大概就是這樣了:
![](http://static.javashuo.com/static/loading.gif)
一樣,如今我要存儲另一個值 「WechatPay」 ,那麼可能映射以後就是下面這樣:
![](http://static.javashuo.com/static/loading.gif)
細心的你可能就會發現, 4 號位置的值,剛開始不是給 「AliPay」 了麼,後來 「WechatPay」 也在那裏,這樣的話,值不就給覆蓋掉了嘛
嗯,沒錯,是被覆蓋掉了
接下來,咱們查詢 「Ali」 那麼查詢以後,布隆過濾器可能會給你 「0,1,2」 的值, 結果呢 「2」 的位置是 0 ,說明沒有任何值映射到這個位置上來,因此咱們就能夠斷定數據庫裏面沒有 「Ali」 這個值
那我查詢 「AliPay」 的話,毫無疑問,確定會返回給我 「1,4,6」 ,那咱們能說數據庫裏面必定有 「AliPay」 麼?不能,由於 「1,4,6」 的值有可能被其餘的值給覆蓋到了,因此咱們只能說,數據庫裏可能存在 「AliPay」
這就是布隆過濾器說的"某個值必定不存在或者可能存在"
乖,你懂了嗎?
< END >
若是你們喜歡咱們的文章,歡迎你們轉發,點擊在看讓更多的人看到。也歡迎你們熱愛技術和學習的朋友加入的咱們的知識星球當中,咱們共同成長,進步。
【推薦閱讀】
本文分享自微信公衆號 - Java極客技術(Javageektech)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。