您好,登錄后才能下訂單哦!
怎么解讀Redis緩存穿透、緩存擊穿和緩存雪崩,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
1、概念
緩存穿透
是指查詢一個數據庫一定不存在的數據
。正常的使用緩存流程大致是,數據查詢先進行緩存查詢,如果key不存在或者key已經過期,再對數據庫進行查詢,并把查詢到的對象,放進緩存。如果數據庫查詢對象為空,則不放進緩存。
這里需要注意緩存擊穿
的區別,緩存擊穿,緩存擊穿是指緩存中沒有但數據庫中有的數據
,并且某一個key非常熱點
,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個key在失效的瞬間(一般是緩存時間到期),持續的大并發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
為了避免緩存穿透其實有很多種解決方案。下面介紹幾種
2、解決方案
(1)布隆過濾器
布隆過濾器
是一個bit向量或者bit,如果我們要映射一個值到布隆過濾器中,我們使用多個不同的哈希函數生成多個哈希值,并將每個生成的哈希值指向的bit位設置為1,如下baidu一詞設置了三個位置為1。
原理:對一個key進行k個hash算法獲取k個值,在比特數組中將這k個值散列后設定為1,然后查的時候如果特定的這幾個位置都為1,那么布隆過濾器判斷該key存在。
“tencent”一詞,對應的情況
可以看到,不同的詞對應的bit位置可能相同,當詞很多的情況時,可能大部分bit位置都是1,這時查詢taobao可能對應的位置都為1,只能說明taobao一詞可能存在,不是一定存在的,這時1就被覆蓋了,這就是布隆過濾器的誤判。如果它說不存在那肯定不存在,如果它說存在,那數據有可能實際不存在。
Redis的bitmap只支持2^32大小,對應到內存也就是512MB,誤判率萬分之一,可以放下2億左右的數據,性能高,空間占用率及小,省去了大量無效的數據庫連接。
因此我們可以通過布隆過濾器,將Redis緩存穿透控制在一個可容范圍內。
使用布隆過濾器:
導入依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
代碼:
public class Test {
private static int size = 1000000;//預計要插入多少數據
private static double fpp = 0.01;//期望的誤判率
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);
public static void main(String[] args) {
//插入數據
for (int i = 0; i < 1000000; i++) {
bloomFilter.put(i);
}
int count = 0;
for (int i = 1000000; i < 2000000; i++) {
if (bloomFilter.mightContain(i)) {
count++;
System.out.println(i + "誤判了");
}
}
System.out.println("總共的誤判數:" + count);
}
}
應用:
@Cacheable(value="key1")
public String get(String key) {
String value = redis.get(key);
// redis中不存在該緩存
if (value == null) {
//布隆過濾器也沒有,直接返回
if(!bloomfilter.mightContain(key)){
return null;
}else{
//布隆過濾器中能查到,不代表一定有,查出來放入redis,同樣也可以避免緩存穿透
value = db.get(key);
redis.set(key, value);
}
}
return value;
}
(2)、緩存空對象
當存儲層不命中后,即使返回的空對象也將其緩存起來,同時會設置一個過期時間,之后再訪問這個數據將會從緩存中獲取,保護了后端數據源。
但是這種方法會存在兩個問題:
?● 如果空值能夠被緩存起來,這就意味著緩存需要更多的空間存儲更多的鍵
,因為這當中可能會有很多的空值的鍵;
?● 即使對空值設置了過期時間,還是會存在緩存層和存儲層的數據會有一段時間窗口的不一致,這對于需要保持一致性的業務會有影響。
(1)、概念緩存雪崩
是指緩存中大批量數據到過期時間
,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是,緩存擊穿指并發查同一條數據
,緩存雪崩是不同數據
都過期了,很多數據都查不到從而查數據庫。
產生雪崩的原因之一,假如馬上就要到雙十一零點,很快就會迎來一波搶購,這波商品時間比較集中的放入了緩存,假設緩存一個小時。那么到了凌晨一點鐘的時候,這批商品的緩存就都過期了。而對這批商品的訪問查詢,都落到了數據庫上,對于數據庫而言,就會產生周期性的壓力波峰。
博主在做電商項目的時候,一般有三種方法:
(1)采取不同分類商品,緩存不同周期。在同一分類中的商品,加上一個隨機因子。這樣能盡可能分散緩存過期時間
,而且,熱門類目的商品緩存時間長一些,冷門類目的商品緩存時間短一些
,也能節省緩存服務的資源。
(2)如果緩存數據庫是分布式部署,將 熱點數據均勻分布在不同的緩存數據庫中。
(3)設置熱點數據永遠不過期。
(4) 使用加鎖限流的方式
(1)概念緩存擊穿
,是指緩存中沒有但數據庫中有的數據
,并且某一個key非常熱點
,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個key在失效的瞬間(一般是緩存時間到期),持續的大并發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
(2)解決方案
?● 設置熱點數據永遠不過期。
?● 使用互斥鎖(mutex key)
??業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作返回成功時,再進行load db的操作并回設緩存;否則,就重試整個get緩存的方法。
SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設置,可以利用它來實現鎖的效果。
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;
}
}
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。