Redis 擊穿、穿透、雪崩產生原因以及解決思路

擊穿

大家都知道, 計算機的瓶頸之一就是 IO, 爲了解決內存與磁盤速度不匹配的問題, 產生了緩存, 將一些熱點數據放在內存中, 隨用隨取, 降低連接到數據庫的請求鏈接, 避免數據庫掛掉。需要注意的是, 無論是擊穿還是後面談到的穿透與雪崩, 都是在高併發前提下, 當緩存中某一個熱點 key 失效,

爲什麼會有擊穿發生呢?

有兩個主要原因:

Key 過期
Key 被頁面置換淘汰

對於第一個原因是因爲在 Redis 中, Key 有過期時間, 如果某一個時刻 (假如商城做活動, 零點開始)key 失效, 那麼零點之後對某一個商品查詢請求將全都壓到數據庫上, 導致數據庫崩。

對於第二個原因, 因爲內存是有限的, 要時時刻刻緩存新的數據, 淘汰舊的數據, 所以在一定的頁面置換策略 (常見頁面置換算法圖解) 中, 淘汰數據, 如果某些商品做活動之前無人問津, 勢必會被淘汰。

應對擊穿的處理思路

正常的處理請求如圖:

由於 key 過期在所難免, 高流量來到 Redis 時, 根據 Redis 的單線程特性, 可以認爲任務是在隊列裏依次執行的, 當請求到達 Redis 發現 Key 過期時, 進行一個操作: 設置鎖

這個流程大概如下:

  1. 請求到達 Redis, 發現 Redis Key 過期, 查看有沒有鎖, 沒有鎖的話回到隊列後面排隊

  2. 設置鎖, 注意, 這兒應該是 setnx(), 而不是 set(), 因爲可能有其他線程已經設置鎖了

  3. 獲取鎖, 拿到鎖了就去數據庫取數據, 請求返回後釋放鎖。

對鎖設置一個過期時間, 如果到達了過期時間還沒釋放就自動釋放, 問題又來了, 鎖掛了好說, 但是如果是鎖超時呢? 也就是在設定的時間裏數據沒有取出來, 但是鎖由過期了, 常見的思路是, 鎖過期時間值遞增, 但是想想不靠譜, 因爲第一個請求可能超時, 如果後面的也超時呢, 接連多次超時之後, 鎖過期時間值勢必特別大了, 這樣做弊端太多。

另外一個思路是, 再開啓一個線程, 進行監控, 如果取數據的線程沒有掛的話, 就適當延遲鎖的過期時間。

穿透

穿透主要原因是很多請求都在訪問數據庫不存在的數據, 例如一個賣書的商城一直被請求查詢茶葉產品, 由於 Redis 緩存主要是用來緩存熱點數據, 對於數據庫都不存在的數據, 是沒法緩存的, 這種異常流量就會直接到達數據庫並且返回 "沒有" 的查詢結果。

應對這種請求, 處理辦法是對訪問請求加一層過濾器, 例如布隆過濾器、增強版布隆過濾器、布穀鳥過濾器, 詳情見:Redis 布隆過濾器與布穀鳥過濾器

除了布隆過濾器, 可以增加一些參數檢驗, 例如數據庫數據 id 一般都是遞增的, 如果請求 id = -10 這種參數, 勢必繞過 Redis, 避免這種情況, 可以對用戶真實性檢驗等操作。

雪崩

雪崩, 和擊穿類似, 不同的是擊穿是一個熱點 Key 某時刻失效, 而雪崩是大量的熱點 Key 在一瞬間失效, 網絡上很多博客都在強調解決雪崩的策略是隨機過期時間, 這個非常不準確, 舉個例子, 銀行做活動, 之前這個利息係數爲 2%, 過了零點係數改爲 3%, 這種情況能將用戶的對應的 key 改爲隨機過期嗎? 如果用的過去的數據叫髒數據。

明顯不可以, 同樣存錢, 你存到年底利息 300 萬, 隔壁才 200 萬, 這不得打架啊, 開玩笑~

正確的思路是, 首先要看看這個 Key 過期是不是時點性有關, 時點性無關的話, 可以隨機過期時間解決。

如果是時點性有關, 例如剛剛說的銀行某一天改變某係數, 那麼就要利用強依賴擊穿方案, 策略是先過去的線程更新一下所有 key

在後臺更新熱點 key 的同時, 業務層將進來的請求延時一下, 例如短暫的睡幾毫秒或者秒, 給後面的更新熱點 key 分散壓力。

作者:等不到的口琴

來源:www.cnblogs.com/Courage129/p/14348720.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/XkdJfwMd-2pMpIa4tZTGrA