redis緩存雪崩 緩存穿透 緩存擊穿如何解決


redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
緩存異常場景分類
在實際生產環境中有時會遇到緩存穿透、緩存擊穿、緩存雪崩等異常場景,為了避免異常帶來巨大損失,我們需要了解每種異常發生的原因以及解決方案,幫助提升系統可靠性和高可用 。
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
緩存穿透
什么是緩存穿透?緩存穿透是指用戶請求的數據在緩存中不存在即沒有命中,同時在數據庫中也不存在,導致用戶每次請求該數據都要去數據庫中查詢一遍,然后返回空 。
如果有惡意攻擊者不斷請求系統中不存在的數據,會導致短時間大量請求落在數據庫上,造成數據庫壓力過大,甚至擊垮數據庫系統 。
緩存穿透常用的解決方案(1)布隆過濾器(推薦)
布隆過濾器(Bloom Filter,簡稱BF)由Burton Howard Bloom在1970年提出,是一種空間效率高的概率型數據結構 。
布隆過濾器專門用來檢測集合中是否存在特定的元素 。
如果在平時我們要判斷一個元素是否在一個集合中,通常會采用查找比較的方法,下面分析不同的數據結構查找效率:
  • 采用線性表存儲,查找時間復雜度為O(N)
  • 采用平衡二叉排序樹(AVL、紅黑樹)存儲,查找時間復雜度為O(logN)
  • 采用哈希表存儲,考慮到哈希碰撞,整體時間復雜度也要O[log(n/m)]
當需要判斷一個元素是否存在于海量數據集合中 , 不僅查找時間慢,還會占用大量存儲空間 。接下來看一下布隆過濾器如何解決這個問題 。
布隆過濾器設計思想
布隆過濾器由一個長度為m比特的位數組(bit array)與k個哈希函數(hash function)組成的數據結構 。位數組初始化均為0,所有的哈希函數都可以分別把輸入數據盡量均勻地散列 。
當要向布隆過濾器中插入一個元素時,該元素經過k個哈希函數計算產生k個哈希值,以哈希值作為位數組中的下標,將所有k個對應的比特值由0置為1 。
當要查詢一個元素時,同樣將其經過哈希函數計算產生哈希值 , 然后檢查對應的k個比特值:如果有任意一個比特為0,表明該元素一定不在集合中;如果所有比特均為1,表明該集合有可能性在集合中 。為什么不是一定在集合中呢?因為不同的元素計算的哈希值有可能一樣,會出現哈希碰撞 , 導致一個不存在的元素有可能對應的比特位為1,這就是所謂“假陽性”(false positive) 。相對地,“假陰性”(false negative)在BF中是絕不會出現的 。
總結一下:布隆過濾器認為不在的,一定不會在集合中;布隆過濾器認為在的,可能在也可能不在集合中 。
舉個例子:下圖是一個布隆過濾器,共有18個比特位,3個哈希函數 。集合中三個元素x,y,z通過三個哈希函數散列到不同的比特位,并將比特位置為1 。當查詢元素w時,通過三個哈希函數計算,發現有一個比特位的值為0,可以肯定認為該元素不在集合中 。
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
布隆過濾器優缺點
優點:
  • 節省空間:不需要存儲數據本身,只需要存儲數據對應hash比特位
  • 時間復雜度低:插入和查找的時間復雜度都為O(k),k為哈希函數的個數
缺點:
  • 存在假陽性:布隆過濾器判斷存在,可能出現元素不在集合中;判斷準確率取決于哈希函數的個數
  • 不能刪除元素:如果一個元素被刪除 , 但是卻不能從布隆過濾器中刪除,這也是造成假陽性的原因了
布隆過濾器適用場景
  • 爬蟲系統url去重
  • 垃圾郵件過濾
  • 黑名單
(2)返回空對象
當緩存未命中,查詢持久層也為空 , 可以將返回的空對象寫到緩存中,這樣下次請求該key時直接從緩存中查詢返回空對象,請求不會落到持久層數據庫 。為了避免存儲過多空對象,通常會給空對象設置一個過期時間 。
這種方法會存在兩個問題:
  • 如果有大量的key穿透,緩存空對象會占用寶貴的內存空間 。
  • 空對象的key設置了過期時間,在這段時間可能會存在緩存和持久層數據不一致的場景 。


redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
緩存擊穿
什么是緩存擊穿?緩存擊穿,是指一個key非常熱點,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大并發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞 。
緩存擊穿危害數據庫瞬時壓力驟增,造成大量請求阻塞 。
如何解決使用互斥鎖(mutex key)
這種思路比較簡單 , 就是讓一個線程回寫緩存,其他線程等待回寫緩存線程執行完,重新讀緩存即可 。
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
同一時間只有一個線程讀數據庫然后回寫緩存 , 其他線程都處于阻塞狀態 。如果是高并發場景,大量線程阻塞勢必會降低吞吐量 。這種情況如何解決?大家可以在留言區討論 。
如果是分布式應用就需要使用分布式鎖 。
熱點數據永不過期
永不過期實際包含兩層意思:
  • 物理不過期 , 針對熱點key不設置過期時間
  • 邏輯過期,把過期時間存在key對應的value里,如果發現要過期了,通過一個后臺的異步線程進行緩存的構建

redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
緩存雪崩
什么是緩存雪崩?緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,請求直接落到數據庫上,引起數據庫壓力過大甚至宕機 。和緩存擊穿不同的是 , 緩存擊穿指并發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫 。
緩存雪崩解決方案常用的解決方案有:
  • 均勻過期
  • 加互斥鎖
  • 緩存永不過期
  • 雙層緩存策略
(1)均勻過期
設置不同的過期時間 , 讓緩存失效的時間點盡量均勻 。通常可以為有效期增加隨機值或者統一規劃有效期 。
(2)加互斥鎖
跟緩存擊穿解決思路一致,同一時間只讓一個線程構建緩存,其他線程阻塞排隊 。
(3)緩存永不過期
跟緩存擊穿解決思路一致,緩存在物理上永遠不過期,用一個異步的線程更新緩存 。
(4)雙層緩存策略
使用主備兩層緩存:
主緩存:有效期按照經驗值設置,設置為主讀取的緩存,主緩存失效后從數據庫加載最新值 。
備份緩存:有效期長,獲取鎖失敗時讀取的緩存 , 主緩存更新時需要同步更新備份緩存 。
緩存預熱
什么是緩存預熱?緩存預熱就是系統上線后,將相關的緩存數據直接加載到緩存系統,這樣就可以避免在用戶請求的時候 , 先查詢數據庫,然后再將數據回寫到緩存 。
如果不進行預熱,那么 Redis 初始狀態數據為空,系統上線初期,對于高并發的流量,都會訪問到數據庫中 ,  對數據庫造成流量的壓力 。
緩存預熱的操作方法
  • 數據量不大的時候,工程啟動的時候進行加載緩存動作;
  • 數據量大的時候,設置一個定時任務腳本,進行緩存的刷新;
  • 數據量太大的時候 , 優先保證熱點數據進行提前加載到緩存 。


redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
緩存降級
緩存降級是指緩存失效或緩存服務器掛掉的情況下,不去訪問數據庫,直接返回默認數據或訪問服務的內存數據 。
在項目實戰中通常會將部分熱點數據緩存到服務的內存中,這樣一旦緩存出現異常,可以直接使用服務的內存數據,從而避免數據庫遭受巨大壓力 。
降級一般是有損的操作 , 所以盡量減少降級對于業務的影響程度 。
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
【redis緩存雪崩 緩存穿透 緩存擊穿如何解決】
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
點分享
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖
redis緩存雪崩 緩存穿透 緩存擊穿如何解決

文章插圖