老生常談的 Redis 雪崩、擊穿、穿透、預熱、降級一次全安排

△Hollis, 一個對Coding有着獨特追求的人△
這是Hollis的第 348 篇原創分享
作者 l zyz1992
來源 l Hollis(ID:hollischuang)
關於 Redis 的介紹、特點什麼的就不再這裏贅述了,不然又要水千把字。今天我們就重點看企業中在使用 Redis 常見一些問題以及對應解決方案。
某個請求到達業務系統,想要獲取某個數據,一般是先從緩存中獲取,如果緩存中不存在就會去數據庫中查詢,如果查詢到結果就將數據保存到緩存中再返回結果。
一個新的技術的引進,必然會帶來一些額外的問題,那麼 Redis 這麼優秀的 NoSQL 數據庫會帶來什麼樣的問題呢?我們一起拭目以待。



緩存擊穿
緩存擊穿根據名字根本無法看懂是什麼意思,並且很容易和另一個詞——緩存穿透搞混。緩存擊穿指的是某個 key 一直在扛着高併發,所謂扛着高併發就是說大量的請求都是獲取這個 key 對應的值。
而這個 key 在某個時間突然失效了,那是不是就意味着大量的請求就無法在緩存中獲取數據了,而是去請求數據庫了,這樣很有可能導致數據庫被擊垮。這就是緩存擊穿。
那現在問題知道了,該如何應對呢?這個就比較簡單了,既然這個 key 這個受歡迎,那麼就不要設置過期時間了,如果該key的數據更新了,那麼就通過互斥鎖的方式將其更新
爲什麼要用互斥鎖的方式?如果不使用互斥鎖的方式很容易導致數據不一致的情況,這裏爲了保證緩存和數據庫的一致性,就只能犧牲一點點的效率了。



緩存雪崩
不知道各位小夥伴都是來自哪裏,我們那邊有句方言叫“雪崩”,表示事情砸了的意思。這裏的Redis 雪崩似乎有點異曲同工之妙。首先我們需要知道什麼是 Redis雪崩
Redis雪 崩我們一般都稱爲緩存雪崩,意思就是說在某個時間節點,大量的 key 失效,導致大量的請求從緩存中獲取不到數據而去請求數據庫。根據上面的那張圖,我們再來畫下雪崩的情況的是什麼樣子的:

上面的黑色的部分表示緩存無效了,也就意味着所有的請求都需要到數據庫中去查詢數據。那這對於數據庫的壓力必然是劇增的,如果是在一線互聯網這樣超高併發的場景下,數據庫直接宕機。
重啓也沒有用,因爲重啓了還會有巨大的流量湧進來,然後繼續被搞宕機。所以對於預防緩存雪崩這種情況的發生意義還是很大的的。
緩存雪崩解決方案之加隨機值
上面已經詳細介紹了什麼是緩存雪崩,他是怎麼發生的,那如果防止緩存雪崩呢?
很簡單,因爲上面剛剛說到,緩存雪崩是由於某個時間節點大量的 key 失效而導致的問題,那現在的問題不就是變成了如何防止同一個時間節點大量的 key 失效這種情況發生嗎?
最簡單的情況就是把key的過期時間分散開,也就是在設置key的過期時間的時候再加一個隨機值,就這樣就能完美的解決緩存雪崩的問題。
但是你以爲我說到這裏就完事了?既然是一次全安排,那麼我一定不會僅僅告訴你一種解決方案就完事的。繼續看
緩存雪崩解決方案之加鎖
可能很多人看到這個方案表示不接受,加鎖那不是限制了併發?加鎖必然導致阻塞。如果是加鎖,那麼執行就成就是這個樣子了:

流程是這樣子的,在多個請求同時到達業務系統時候,只能有一個線程能獲取到鎖,然後才能繼續去緩存或者是數據庫中查詢數據,然後後面的流程和之前的是一樣的,執行完成後釋放鎖,然後其他線程再爭搶鎖,然後重複前面的流程。
這個方案的優點是可以很好的保護數據庫不會被打掛,缺點就是併發度極低。
上面這個方案其實還是可以再優化下的:

這個就是在緩存中如果獲取不到,再去串行的訪問數據看,這裏不一定非要串行,可以配合線程池,控制一定的併發數。
這個缺點雖然很多,但是也是一種解決方案。用不用就看實際的業務場景了。畢竟沒有沒用技術方案,只有不適合業務場景的技術方案(手動狗頭)



緩存穿透
緩存穿透意思就是某個不存在的key一直被訪問,結果發現數據庫中也沒有這樣的數據,最終導致訪問該key的所有請求都直接請求到數據庫了。如果是併發高的場景下就容易搞垮數據庫。大家有沒有發現我們做的一些事情都是在保護“弱小的數據庫”。
那現在問題已經知道了,我們該如何去解決這個問題呢?
緩存穿透解決方案之緩存空數據
啥叫緩存空數據?就是假設某個key數據並不存在,那麼就存一個 NULL 就好了,但是一定不要忘記設置過期時間,因爲假設id=3的記錄不存在,然後本次訪問沒有查詢到數據,緩存中存的是null如果過一會兒新增了一條記錄爲3的數據,如果緩存不設置過期時間,那麼這條數據就永遠獲取不到。
緩存穿透解決方案之布隆過濾器
布隆過濾器?這玩意到底什麼意思?
布隆過濾器是一種數據結構,更準確的說是一種概率型的數據結構,因爲它能判斷某個元素一定不存在或者是可能存在
就這句話,搞蒙了很多人,今天我非要把你說明白了。布隆過濾器是一個bit數組,一個很長的bit數組和一系列的hash函數構成。先看下圖

我們現在來舉個例子,假設現在有小強旺財兩個人,他們分別經過三次hash得到的下標是這樣子的(布隆過濾器不存儲元素,僅僅是爲一個元素是否存在打一個標誌)
小強經過上面的三個hash後得到的下標分別爲:2、4、5,那麼該數組的2、4、5位置就會被置爲1,也就是此時是這樣子的

同樣旺財經過上面的三個hash後得到的下標分別爲:3、7、11,那麼該數組的3、7、11位置就會被置爲1,也就是此時是這樣子的

現在假設來一個 007 經過上面的三個hash後得到的下標分別爲:11、13、15因爲13、和15位置是0,所以一定可以判斷007 一定不存在。但是現在又來了一個
9527經過上面的三個hash後得到的下標分別爲:2、5、7,但是你會發現257三個位置全部是1,那這個到底說明9527是存在還是不存在呢?
從我們上面的講解可以 9527 之前並不存在,但是由於hash衝突,但是9527的三個下標值也剛好落在已經被置爲1的下標位置,這就導致此時是無法判斷9527是否存在的。這就是布隆過濾器的原理。
要不來段代碼壓壓驚?
我們來使用 google 包下的類來測試。首先要添加依賴

<dependency>

    <groupId>com.google.guava</groupId>

    <artifactId>guava</artifactId>

    <version>30.1-jre</version>

</dependency>

代碼如下(詳細的解釋我已經寫在註釋中了,這個是可以用於實際生產的代碼)

public class BloomFilterDemo {

    public static void main(String[] args) {

        /**

         * 創建一個插入對象爲一億,誤報率爲0.01%的布隆過濾器

         * 不存在一定不存在

         * 存在不一定存在

         */


        BloomFilter&lt;CharSequence&gt; bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")),

                100000000,

                0.0001);

        bloomFilter.put("死");

        bloomFilter.put("磕");

        bloomFilter.put("Redis");

        System.out.println(bloomFilter.mightContain("Redis"));

        System.out.println(bloomFilter.mightContain("死"));

        System.out.println(bloomFilter.mightContain("磕"));

        System.out.println(bloomFilter.mightContain("Java"));

    }

}

結果

完。
等等……緩存穿透、預熱、降級你還沒說呢。哦,我真的以爲本文結束了。
那布隆過濾器是如何解決緩存穿透的問題的呢?既然已經知道了布隆過濾器的原理,那麼就可以通過布隆過濾器來快速的判斷出一個key是否存在數據庫中,如果可能存在再去數據庫查詢,如果布隆過濾器中不存在那麼就需要再去數據庫查詢了



緩存預熱
這又是什麼鬼?怎麼搞一個緩存還有這麼多問題,那還要緩存幹啥?
所謂緩存預熱就是將一些可能經常使用數據在系統啓動的時候預先設置到緩存中,這樣可以避免在使用到的時候先去數據庫中查詢。
這就是緩存預熱,名氣高大上,實際上很簡單有木有,這個緩存預熱我在實際場景是經常使用的。
還有一種方式就是添加一個緩存刷新頁,這樣通過人工干預的方式將一些可能爲熱點的key添加到緩存中。





緩存降級
當訪問量突然劇增(例如下班的點,大家都在地鐵上刷手機呢)、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的性能時,仍然需要保證服務還是可用的,即使是有損服務。
系統可以根據一些關鍵數據進行自動降級,降級的最終目的是保證核心服務可用,即使是有損的。但是有的一些業務的核心服務是不能降級的。這是一種丟卒保帥的思想。



結束語
關於技術的學習,大家除了爲了應付面試去短期強行的記憶一些知識點外,我還是建議各位在學習階段能夠循序漸進。小孩子從出生到走路一般還有10個月呢,要想會說話時間就更長。
但是這個過程必須是有的,因爲小孩子需要一點一點來適應這個未知的世界。我們作爲成年人在學習的時候也要保持這種平靜心態,有些事情急是沒用的。
最後以一句不畏艱險,勇攀高峯來和大家共勉。



                        
                        
                        

往期推薦

曝光!某銀行軟件開發中心拖欠工資,招聘套路深!防不勝傷!!


絕了!這款工具讓 Spring Boot 不在需要 Controller、Service、DAO、Mapper 了


黑客用GitHub服務器挖礦,三天跑了3萬個任務,代碼驚現中文



 

直面Java第343期:爲什麼TOMCAT要破壞雙親委派

深入併發第013期:拓展synchronized——鎖優化


如果你喜歡本文,
請長按二維碼,關注 Hollis.
轉發至朋友圈,是對我最大的支持。

點個 在看 
喜歡是一種感覺
在看是一種支持
↘↘↘

本文分享自微信公衆號 - Hollis(hollischuang)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章