一提到Redis緩存,我們不得不了解的三個問題就是:緩存雪崩、緩存擊穿和緩存穿透。這三個問題一旦發生,會導致大量的請求直接請求到數據庫層。如果並發壓力大,就會導致數據庫崩潰。那p0級的故障是沒跑了。
今天我們就來詳細的了解這個三個問題誘因以及如何解決。
廢話不多說,我們直接開搞!!!
一、緩存雪崩
什么是緩存雪崩?緩存雪崩就是大量請求無法在redis緩存中進行處理,而是直接發送到了數據庫層,使得數據庫壓力陡增。就好像redis一下子突然失效了一樣。一般造成緩存雪崩主要有兩個原因,我們來一一分析一下。
1.緩存中大量數據同時過期
緩存中大量數據同時過期,就會導致大量請求無法在redis緩存層面進行處理。具體來說,就是給redis中大量數據設置了相同的過期時間,一旦它們同時失效,應用就會把請求直接發送給數據庫,直接從數據庫中讀取數據。如果應用的並發量很大,那數據庫的壓力就會很大。如下圖所示:
針對大量數據同時失效帶來的緩存雪崩問題,我們一般采取以下兩種解決方案。 (1)我們在開發過程中要避免給大量數據設置相同的過期時間。我們可以在給數據設置過期時間時給時間加一個很小的隨機數,這樣不同數據的過期時間就會有所差別,但差別也不會太大,保證數據在一定范圍內過期,從而滿足業務層要求同時過期的需要。 (2)服務降級。所謂的服務降級,是指發生緩存雪崩后,針對不同的數據采取不同的策略。
- 當業務訪問非核心數據時(例如商品屬性信息),我們直接返回預定義的信息。
- 當業務訪問的是如庫存數據等核心數據時,仍然允許查詢緩存,如果緩存缺失,也可以從數據庫中繼續讀取。
這樣一來,只有部分過期的數據會訪問數據庫,所以數據庫壓力就沒那么大。
2.Redis實例發生故障
當Redis實例發生故障,那就相當於緩存已經廢掉了,所以大量請求會直接請求數據庫,造成數據庫壓力變大,甚至宕機。針對這種情況發生的緩存雪崩,我們有以下兩種處理方式。 (1)在業務系統側實現服務熔斷或請求限流機制 所謂的服務熔斷,就是指在發生緩存雪崩時,為了防止大流量直接打到數據庫,我們會暫停對緩存系統的訪問。當上層應用訪問緩存時,緩存接口不會去訪問Redis實例,而是直接返回。等redis恢復后,再允許應用程序請求緩存系統。這樣就會避免因為redis緩存宕機,導致數據庫壓力陡增的情況。
服務熔斷雖然可以保證數據庫不被崩潰,但是暫停了整個服務的訪問,對業務的影響范圍大,為了減小對上層服務的影響,我們一般采用請求限流。請求限流是指業務系統去控制每秒進入系統的請求數,避免過多的請求被發送到數據庫。比如正常運行時,業務系統每秒進入的請求是1萬個,其中有80%在緩存中就可以處理了,有20%會去數據庫中處理。一旦發生緩存雪崩,100%的流量就會請求數據庫,為了不造成數據庫崩潰,我們就可以啟動請求限流機制。業務系統只允許30%的流量進入,而70%的流量被拒絕服務。這也是目前主流大廠常用的方法,比如在某個明星爆出大瓜后,我們刷微博經常刷不出來,多刷幾次就能進入,那就是因為做了服務降級。只允許一部分流量進入。
(2)使用高可靠集群
我們可以通過主從節點來部署高可靠的Redis集群。當主節點掛掉后,從節點還可以切換成主節點。
二、緩存擊穿
緩存擊穿是指針對某個熱點數據,無法在緩存中進行處理,然后訪問該數據的大量請求,一下子都發到后端數據庫中,導致數據庫壓力激增。對於緩存擊穿的情況,經常發生在熱點數據過期失效時。
為了避免這種情況發生,最常采取的措施就是對於訪問特別頻繁的熱點數據,我們就不設置過期時間了。這樣一來,對熱點數據的訪問,都可以在緩存中進行。
三、緩存穿透
緩存穿透是指要訪問的數據既不在緩存中,也不在數據庫中,會導致請求緩存時,發生緩存缺失,然后請求數據庫,發現數據庫中也沒有需要的數據。這樣一來,緩存就成了“擺設”,如果有大量的這種請求,就會給數據庫帶來很大的壓力。
這個問題一般都是黑客進行惡意攻擊造成的。為了避免這種問題發生,我們有三種解決方式。
1、緩存空值或者缺省值
一旦發生緩存穿透,我們就可以在redis中設置一個空值或者給定的某個缺省值。這樣,業務應用的后續這種請求,都可以命中緩存。這樣就避免了把大量請求發送給數據庫了。
2、使用布隆過濾器來快速判斷數據是否存在
這里我們先來解釋一下什么是布隆過濾器。
布隆過濾器由一個初值都為0的bit數組和N個哈希函數組成,可以用來快速判斷某個數據是否存在。當我們想標記某個數據存在時,布隆過濾器會通過三個操作來完成標記:
- 首先,使用N個哈希函數,分別計算數據的哈希值,得到N個哈希值。
- 然后把這N個哈希值對bit數組的長度取模,得到每個哈希值在數組中的位置。
- 最后,我們把對應位置的bit位設置為1,這樣就完成了布隆過濾器中標記數據的操作。
如果數據不存在,也就是我們沒有用布隆過濾器標記過,bit數組對應的bit位為0。 當我們需要判斷某個數據是否存在時,我們就執行上面的計算過程,我們先求出這個數據對應的hash值,然后取模,然后去bit數組查這N個位置上的bit值。只要這N個bit值有一個不為1,就表明這個數據沒有被標記過。 基於布隆過濾器的快速檢測特性,我們可以把數據寫入數據庫時,使用布隆過濾器做個標記,當緩存失效后,上層應用查詢數據庫時,可以通過查詢布隆過濾器快速判斷數據是否存在。如果不存在,就不用在去數據庫中去查了。這樣一來,即使發生緩存穿透,也不會對數據庫造成壓力。
3、業務層對請求進行檢測
緩存穿透發生的原因主要就是惡意請求訪問不存在的數據,所以業務層接受到請求后,一定要進行合法性檢測,把惡意請求給過濾掉,這樣就可以避免緩存穿透的問題了。
今天我們就聊到這里,如果感興趣,記得關注一波公眾號【程序員學長】,有你意想不到的收獲哦。