玩轉Redis內存數據庫:一篇文章搞懂Redis緩存穿透、擊穿、雪崩

Redis用於做緩存是非常棒的,內存數據庫可比MySQL這些會產生IO讀取的快樂不知道多少倍。但是Redis也還是會面臨著如下幾種問題:緩存穿透、擊穿和雪崩;下面就簡單分析下原因和解決方案。

1、緩存穿透

什麼是緩存穿透呢,首先我們知道,我們用redis做緩存的大概邏輯如下:

<code>1、根據KEY取Redis中讀取,若是有值則直接返回,否則繼續下一步根據key去數據庫中查找,
2、若是有值,則將結果放入redis,然後返回,否則數據不存在/<code>

那麼上面會面臨什麼問題呢?假如攻擊者(這種情況一般是因為有攻擊者來攻擊,正常業務邏輯不會存在數據庫中沒有值的情況)直接大量調用該接口,隨機構造key,然後這些key在我們的數據庫中都沒有對應值,這樣子就會每次都直接訪問到數據庫,redis就失去了緩存的意義,給數據庫造成了巨大的壓力。因此,緩存穿透的大概意思是:key對應的數據在數據源並不存在,每次針對此key的請求從緩存獲取不到,請求都會到數據源,從而可能壓垮數據源。

解決方案

1、我們知道,緩存穿透基本上都是有人在對系統進行攻擊才會發生的,正常業務邏輯不會發生,所以根本上的解決方案是:

實現API限流,防禦DDOS攻擊,接口頻率訪問控制。只有直接限制攻擊者的訪問頻率,把不合法的攻擊者假如到黑名單中,才是最根本有效的解決方案。

2、從數據庫中查詢不到值的時候,也將key寫入redis,並且值設置為一個較短的有效期。這種方案其實是不靠譜的,首先這個只適用於相同的KEY,假如攻擊者一直隨機生成不同的key就攔不到了,並且會對正常的redis使用造成影響,不過還是會有一點點用的。

3、布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力

2、緩存擊穿

緩存擊穿是什麼意思呢,這個其實是對於單個熱點KEY的情況下,在高併發訪問此“熱點KEY”,但是該KEY過期了,此時會有大量的訪問直接訪問數據庫,壓垮我們的數據源。比如在抽獎秒殺等活動中,法發生的概率很高。

解決方案

這裡我覺得最好的解決辦法是採用分佈式鎖吧,擊穿導致的是數據庫被壓垮,是因為都一起訪問數據庫,但是如果我們用分佈式鎖,限定只有一個請求去訪問數據庫,將值從數據庫中讀取回來放入redis中,然後其它請求就直接使用redis中的值啦,如下是redis實現分佈式鎖的一個代碼例子,很簡單的,主要是靠:setNx命令,當然也還可以用zookeeper。

<code>public String get(key) {
String value = redis.get(key);
if (value == null) { //代表緩存值過期
//設置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表設置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //這個時候代表同時候的其他線程已經load db並回設到緩存了,這時候重試獲取緩存值即可
sleep(50);
get(key); //重試
}
} else {
return value;
}
}/<code>

3、緩存雪崩

什麼是緩存雪崩呢?,與緩存擊穿的區別在於這裡針對很多key緩存,前者則是某一個key。比如redis中的一批key同時間失效了,這樣子就會全部key都得同時區請求數據庫,最後壓垮數據庫。

解決辦法

對key的失效時間進行隨機化設置。既然是因為同時間失效,導致都同時去讀取數據庫,那麼我們對key的失效時間假如一個隨機數,比如1~5分鐘失效,這樣子就很小概率會出現同時失效的情況。


分享到:


相關文章: