1、緩存穿透
指緩存和數據庫中都沒有的數據,而用戶不斷發起請求。由于緩存不命中,并且出于容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,緩存就沒有意義了。
在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。
解決方案
- 從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒。
- 布隆過濾器。bloomfilter就類似于一個hash set,用于快速判某個元素是否存在于集合中,其典型的應用場景就是快速判斷一個key是否存在于某容器,不存在就直接返回。
2、緩存擊穿
主要針對的是熱點key
指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由于并發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力。
解決方案
- 設置熱點數據永遠不過期。
- 接口限流與熔斷,降級。重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級準備,當接口中的某些 服務 不可用時候,進行熔斷,失敗快速返回機制。
- 加互斥鎖。
3、緩存雪崩
指緩存中數據大批量過期,而查詢數據量巨大,引起數據庫壓力過大甚至down機。
緩存擊穿指并發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。
解決方案
- 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
- 如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同的緩存數據庫中。
- 設置熱點數據永遠不過期。
4、緩存污染
緩存污染問題說的是緩存中一些只會被訪問一次或者幾次的的數據,被訪問完后,再也不會被訪問到,但這部分數據依然留存在緩存中,消耗緩存空間。
緩存污染會隨著數據的持續增加而逐漸顯露,隨著服務的不斷運行,緩存中會存在大量的永遠不會再次被訪問的數據。緩存空間是有限的,如果緩存空間滿了,再往緩存里寫數據時就會有額外開銷,影響Redis性能。
一般性建議緩存容量設置為總數據量的 15% 到 30%,兼顧訪問性能和內存空間開銷。
5、緩存淘汰策略
主要分成三大類
- 不淘汰:noeviction (v4.0后默認的)
- 設置了過期時間的數據:
隨機:volatile-random
ttl:volatile-ttl
lru:volatile-lru
lfu:volatile-lfu - 全部淘汰:
隨機:allkeys-random
lru:allkeys-lru
lfu:allkeys-lfu
5.1、緩存淘汰策略詳解
- noeviction: 該策略是Redis的默認策略,一旦緩存被寫滿了,再有寫請求來時,Redis 不再提供服務,而是直接返回錯誤。這種策略不會淘汰數據,所以無法解決緩存污染問題。一般生產環境不建議使用。
- volatile-random: 在設置了過期時間的鍵值對中,進行隨機刪除。因為是隨機刪除,無法把不再訪問的數據篩選出來,所以可能依然會存在緩存污染現象,無法解決緩存污染問題。
- volatile-ttl: 參考的指標比隨機刪除時多進行一步過期時間的排序。Redis在篩選需刪除的數據時,越早過期的數據越優先被選擇。
- volatile-lru: 按照最近最少使用的原則來篩選數據。這種模式下會使用 LRU 算法篩選設置了過期時間的鍵值對。
- volatile-lfu: 使用 LFU 算法選擇設置了過期時間的鍵值對.
LFU 算法:LFU 緩存策略是在 LRU 策略基礎上,為每個數據增加了一個計數器,來統計這個數據的訪問次數。當使用 LFU 策略篩選淘汰數據時,首先會根據數據的訪問次數進行篩選,把訪問次數最低的數據淘汰出緩存。如果兩個數據的訪問次數相同,LFU 策略再比較這兩個數據的訪問時效性,把距離上一次訪問時間更久的數據淘汰出緩存。
6、數據庫和緩存一致性問題
加入redis換粗后的一般的業務場景如下:
讀取緩存步驟一般沒有什么問題,但是一旦涉及到數據更新:數據庫和緩存更新,就容易出現緩存(Redis)和數據庫(MySQL)間的數據一致性問題。
不管是先寫MySQL數據庫,再刪除Redis緩存;還是先刪除緩存,再寫庫,都有可能出現數據不一致的情況。
總結:
讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然后取出數據后放入緩存,同時返回響應。
更新的時候,先更新數據庫,然后再刪除緩存。
6.1、緩存一致性的解決方案
在更新數據庫成功后,刪除緩存key不一定生效的,解決方案是什么?
方案1:隊列 + 重試機制
流程如下所示:
1.更新數據庫數據;
2.緩存因為種種問題刪除失敗
3.將需要刪除的key發送至消息隊列
4.自己消費消息,獲得需要刪除的key
5.繼續重試刪除操作,直到成功
該方案有一個缺點,對業務線代碼造成大量的侵入。于是有了方案2
方案2:異步更新緩存(基于訂閱binlog的同步機制)
整體思路:MySQL binlog增量訂閱消費+消息隊列+增量數據更新到redis