Redis 擊穿、穿透、雪崩產生原因以及解決思路
擊穿
大家都知道, 計算機的瓶頸之一就是 IO, 爲了解決內存與磁盤速度不匹配的問題, 產生了緩存, 將一些熱點數據放在內存中, 隨用隨取, 降低連接到數據庫的請求鏈接, 避免數據庫掛掉。需要注意的是, 無論是擊穿還是後面談到的穿透與雪崩, 都是在高併發前提下, 當緩存中某一個熱點 key 失效,
爲什麼會有擊穿發生呢?
有兩個主要原因:
Key 過期
Key 被頁面置換淘汰
對於第一個原因是因爲在 Redis 中, Key 有過期時間, 如果某一個時刻 (假如商城做活動, 零點開始)key 失效, 那麼零點之後對某一個商品查詢請求將全都壓到數據庫上, 導致數據庫崩。
對於第二個原因, 因爲內存是有限的, 要時時刻刻緩存新的數據, 淘汰舊的數據, 所以在一定的頁面置換策略 (常見頁面置換算法圖解) 中, 淘汰數據, 如果某些商品做活動之前無人問津, 勢必會被淘汰。
應對擊穿的處理思路
正常的處理請求如圖:
由於 key 過期在所難免, 高流量來到 Redis 時, 根據 Redis 的單線程特性, 可以認爲任務是在隊列裏依次執行的, 當請求到達 Redis 發現 Key 過期時, 進行一個操作: 設置鎖
這個流程大概如下:
-
請求到達 Redis, 發現 Redis Key 過期, 查看有沒有鎖, 沒有鎖的話回到隊列後面排隊
-
設置鎖, 注意, 這兒應該是 setnx(), 而不是 set(), 因爲可能有其他線程已經設置鎖了
-
獲取鎖, 拿到鎖了就去數據庫取數據, 請求返回後釋放鎖。
對鎖設置一個過期時間, 如果到達了過期時間還沒釋放就自動釋放, 問題又來了, 鎖掛了好說, 但是如果是鎖超時呢? 也就是在設定的時間裏數據沒有取出來, 但是鎖由過期了, 常見的思路是, 鎖過期時間值遞增, 但是想想不靠譜, 因爲第一個請求可能超時, 如果後面的也超時呢, 接連多次超時之後, 鎖過期時間值勢必特別大了, 這樣做弊端太多。
另外一個思路是, 再開啓一個線程, 進行監控, 如果取數據的線程沒有掛的話, 就適當延遲鎖的過期時間。
穿透
穿透主要原因是很多請求都在訪問數據庫不存在的數據, 例如一個賣書的商城一直被請求查詢茶葉產品, 由於 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