緩存問題:一致性、穿擊、穿透、雪崩、污染

  1. 爲什麼要理解Redi緩存問題

(1) 在高併發的業務場景下,數據庫大多數情況都是用戶併發訪問最薄弱的環節。所以,就需要使用redis做一個緩衝操作,讓請求先訪問到redis,而不是直接訪問mysql等數據庫。這樣可以大大緩解數據庫的壓力

(2) 當緩存庫出現時,必須要考慮如下問題

① 緩存穿透

② 緩存穿擊

③ 緩存雪崩

④ 緩存污染(或者慢了)

⑤ 緩存和數據庫一致性

  1. 緩存穿透

(1) 原因

① 緩存穿透是指緩存和數據庫中都沒有數據,而用戶不斷髮起請求。由於緩存時不命中時被動寫的,並且出於容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要去存儲層查詢,失去了緩存的意義。在流量比較大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊應用,這就是漏洞;如果發起id=-1的數據或者id特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。

(2) 解決方案

① 接口增增加校驗,如用戶鑑權校驗,id做基礎校驗,id<=0的直接攔截

② 從緩存取不到的數據,在數據庫中也沒有取到,這個也可以將key-value對寫成key-null,緩存有效時間可以短點。這樣可以防止攻擊用戶反覆用同一個id暴力攻擊

③ 布隆過濾器。bloomfilter就類似於一個hash set,用於快速判斷某個元素是否存在於集合中,其典型的應用場景就是快速判斷一個key是否存在於某容器,不存在就直接返回。布隆過濾器的關鍵就在於hash算法和容器大小

  1. 緩存擊穿

(1) 問題來源

① 緩存擊穿是指緩存中沒有但是數據庫中有數據(一般是緩存時間到期),這時由於併發用戶特別多,同時讀緩存又沒有讀到數據,又同時去數據庫取數據,引起數據庫壓力瞬間增大,造成過大壓力

(2) 解決方案

① 設置熱點數據永不過期

② 接口限流與熔斷、降級。重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級準備,當接口中的某些服務不可用時,進行熔斷、失敗快速返回機制

③ 加互斥鎖

  1. 緩存雪崩

(1) 問題來源

① 緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至宕機。和緩存擊穿不同的是,緩存擊穿指併發查同一條數據,緩存雪崩是不同的數據都過期了,很多數據都查不到而查數據庫了

(2) 解決方案

① 緩存數據的過期時間選擇隨機,防止同一時間大量數據過期現象發生

② 如果緩存數據庫是分佈式緩存,將熱點數據均勻分佈在不同的緩存數據庫中

③ 設置熱點數據永不過期

  1. 緩存污染(或滿了)

(1) 緩存污染問題說的是緩存中一些只會被訪問一次或者幾次的數據,被訪問完後,再也不會被訪問到,但是這些數據依然存留在緩存中,消耗緩存空間

(2) 緩存污染會隨着數據的持續增加而逐漸顯露,隨着服務的不斷運行,緩存中會存在大量的永遠不會被再次訪問的數據。緩存空間是有限的,如果緩存空間滿了,再往緩存裏寫數據時就會又額外開銷,影響Redis性能。這部分額外開銷主要是指寫的時候判斷淘汰策略,根據淘汰策略去選擇要淘汰的數據,然後進行刪除操作

  1. 最大緩存設置多大

(1) 系統的設計選擇是一個權衡的過程;大容量緩存是能帶來性能加速的收益,但是成本也會更高,而小容量緩存不一定就起不到加速訪問的效果。一般來說建議把緩存容量設置爲總數據量的15%-30%,兼顧訪問性能和內存空間開銷

(2) 對於Redis來說,一旦確定了最大緩存容量,比如4GB,就可以通過命令來設置

(3) CONFIG SET maxmemory 4gb

  1. 數據庫和緩存一致性

(1) 問題來源

① 使用Redis做一個緩衝操作哦,讓請求前訪問到redis,而不是直接訪問mysql等數據庫

 

 

 

讀取緩存步驟一般沒有什麼問題,但是一旦涉及到數據更新:數據庫和緩存更新,就容易出現緩存和數據庫之間的數據一致性問題

不管是先寫mysql數據庫,再刪除Redis緩存;還是先刪除緩存再寫庫,都可能出現數據不一致情況

② 實例

1) 如果刪除了緩存Redis,還沒有來得及寫庫mysql,另外一個線程就來讀取,發現緩存爲空,則去數據庫中讀取數據寫入緩存,此時緩存中爲髒數據

2) 如果先寫了庫,再刪除緩存前,寫庫的線程宕機了,沒有刪除掉緩存,則也會出現數據不一致的情況

3) 因爲讀和寫是併發的,沒法保證順序,就會出現緩存 和數據庫的數據不一致問題

  1. 四種相關模式

(1) 分類

① Cache aside

② Read through

③ Write through

④ Write behind caching

(2) 最常用的Cache aside,總結

① 讀的時候 先讀緩存,緩存沒有的話,就讀數據庫,然後讀出數據後放入緩存,同時返回響應

② 更新的時候 先更新數據庫,然後在刪除緩存

③ 其具體邏輯如下

1) 失效:應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功後放入到緩存中

2) 命中:應用程序從cache中取數據,取到後返回

3) 更新:先把數據存儲到數據庫中,成功後再讓緩存失效

4) 圖示

 

 

 

④ 具體實現方案1:隊列+重試機制

1) 圖示

 

 

 

 

 

2) 流程如下:

  1. 更新數據庫數據
  2. 緩存因爲種種問題刪除失敗
  3. 將需要刪除的key發送到消息隊列
  4. 自己消費消息,獲得需要刪除的key
  5. 繼續重試刪除操作,直到成功

3) 缺點

  1. 對業務線代碼造成大量入侵

⑤ 具體實現方案2:異步更新緩存(基於訂閱binlog的同步機制)

1) 圖示

 

 

 

 

2) 技術整體思路

  1. Mysql binlog增量訂閱消費+消息隊列+增量數據更新到Redis

a) Redis:熱點數據基本都在Redis

b) Mysql:增刪改都是操作Mysql

c) 更新Redis數據:Mysql的數據操作binlog,來更新到Redis

3) Redis更新

  1. 數據操作主要分爲兩大塊

a) 一個是全量(將所有數據一次性寫入到redis)

b) 一個是增量(實時更新)

  1. 讀取binlog後分析,利用消息隊列,推送更新各臺的redis緩存數據

a) 這樣一旦Mysql中產生了新的寫入、更新、刪除等操作,就可以把binlog相關的消息推送至Redis,Redis再根據binlog中的 記錄,對Redis進行更新。其實這種機制,很類似於Mysql的主從備份機制,因爲Mysql也通過binlog來實現數據的一致性

b) 消息推送工具:canalkafkarabbitMQ

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