日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

缓存三大问题及解决方案

發(fā)布時(shí)間:2023/12/31 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 缓存三大问题及解决方案 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

1. 緩存來由

隨著互聯(lián)網(wǎng)系統(tǒng)發(fā)展的逐步完善,提高系統(tǒng)的qps,目前的絕大部分系統(tǒng)都增加了緩存機(jī)制從而避免請(qǐng)求過多的直接與數(shù)據(jù)庫操作從而造成系統(tǒng)瓶頸,極大的提升了用戶體驗(yàn)和系統(tǒng)穩(wěn)定性。

2. 緩存問題

雖然使用緩存給系統(tǒng)帶來了一定的質(zhì)的提升,但同時(shí)也帶來了一些需要注意的問題。

2.1 緩存穿透

緩存穿透是指查詢一個(gè)一定不存在的數(shù)據(jù),因?yàn)榫彺嬷幸矡o該數(shù)據(jù)的信息,則會(huì)直接去數(shù)據(jù)庫層進(jìn)行查詢,從系統(tǒng)層面來看像是穿透了緩存層直接達(dá)到db,從而稱為緩存穿透,沒有了緩存層的保護(hù),這種查詢一定不存在的數(shù)據(jù)對(duì)系統(tǒng)來說可能是一種危險(xiǎn),如果有人惡意用這種一定不存在的數(shù)據(jù)來頻繁請(qǐng)求系統(tǒng),不,準(zhǔn)確的說是攻擊系統(tǒng),請(qǐng)求都會(huì)到達(dá)數(shù)據(jù)庫層導(dǎo)致db癱瘓從而引起系統(tǒng)故障。

2.2 解決方案

緩存穿透業(yè)內(nèi)的解決方案已經(jīng)比較成熟,主要常用的有以下幾種:

  • bloom filter:類似于哈希表的一種算法,用所有可能的查詢條件生成一個(gè)bitmap,在進(jìn)行數(shù)據(jù)庫查詢之前會(huì)使用這個(gè)bitmap進(jìn)行過濾,如果不在其中則直接過濾,從而減輕數(shù)據(jù)庫層面的壓力。
  • 空值緩存:一種比較簡(jiǎn)單的解決辦法,在第一次查詢完不存在的數(shù)據(jù)后,將該key與對(duì)應(yīng)的空值也放入緩存中,只不過設(shè)定為較短的失效時(shí)間,例如幾分鐘,這樣則可以應(yīng)對(duì)短時(shí)間的大量的該key攻擊,設(shè)置為較短的失效時(shí)間是因?yàn)樵撝悼赡軜I(yè)務(wù)無關(guān),存在意義不大,且該次的查詢也未必是攻擊者發(fā)起,無過久存儲(chǔ)的必要,故可以早點(diǎn)失效。

2.3 緩存雪崩

在普通的緩存系統(tǒng)中一般例如redis、memcache等中,我們會(huì)給緩存設(shè)置一個(gè)失效時(shí)間,但是如果所有的緩存的失效時(shí)間相同,那么在同一時(shí)間失效時(shí),所有系統(tǒng)的請(qǐng)求都會(huì)發(fā)送到數(shù)據(jù)庫層,db可能無法承受如此大的壓力導(dǎo)致系統(tǒng)崩潰。

2.4 解決方案

  • 線程互斥:只讓一個(gè)線程構(gòu)建緩存,其他線程等待構(gòu)建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)才可以,每個(gè)時(shí)刻只有一個(gè)線程在執(zhí)行請(qǐng)求,減輕了db的壓力,但缺點(diǎn)也很明顯,降低了系統(tǒng)的qps。
  • 交錯(cuò)失效時(shí)間:這種方法時(shí)間比較簡(jiǎn)單粗暴,既然在同一時(shí)間失效會(huì)造成請(qǐng)求過多雪崩,那我們錯(cuò)開不同的失效時(shí)間即可從一定長(zhǎng)度上避免這種問題,在緩存進(jìn)行失效時(shí)間設(shè)置的時(shí)候,從某個(gè)適當(dāng)?shù)闹涤蛑须S機(jī)一個(gè)時(shí)間作為失效時(shí)間即可。

2.5 緩存擊穿

緩存擊穿實(shí)際上是緩存雪崩的一個(gè)特例,大家使用過微博的應(yīng)該都知道,微博有一個(gè)熱門話題的功能,用戶對(duì)于熱門話題的搜索量往往在一些時(shí)刻會(huì)大大的高于其他話題,這種我們成為系統(tǒng)的“熱點(diǎn)“,由于系統(tǒng)中對(duì)這些熱點(diǎn)的數(shù)據(jù)緩存也存在失效時(shí)間,在熱點(diǎn)的緩存到達(dá)失效時(shí)間時(shí),此時(shí)可能依然會(huì)有大量的請(qǐng)求到達(dá)系統(tǒng),沒有了緩存層的保護(hù),這些請(qǐng)求同樣的會(huì)到達(dá)db從而可能引起故障。擊穿與雪崩的區(qū)別即在于擊穿是對(duì)于特定的熱點(diǎn)數(shù)據(jù)來說,而雪崩是全部數(shù)據(jù)。

2.6 解決方案

1.使用互斥鎖(mutex key)
業(yè)界比較常用的做法,是使用mutex。簡(jiǎn)單地來說,就是在緩存失效的時(shí)候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者M(jìn)emcache的ADD)去set一個(gè)mutex key,當(dāng)操作返回成功時(shí),再進(jìn)行l(wèi)oad db的操作并回設(shè)緩存;否則,就重試整個(gè)get緩存的方法。

SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時(shí)候才設(shè)置,可以利用它來實(shí)現(xiàn)鎖的效果。在redis2.6.1之前版本未實(shí)現(xiàn)setnx的過期時(shí)間,所以這里給出兩種版本代碼參考:

//2.6.1前單機(jī)版本鎖
String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, 3 * 60)
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他線程休息50毫秒后重試
Thread.sleep(50);
get(key);
}
}
}
最新版本代碼:
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表緩存值過期
//設(shè)置3min的超時(shí),防止del操作失敗的時(shí)候,下次緩存過期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表設(shè)置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //這個(gè)時(shí)候代表同時(shí)候的其他線程已經(jīng)load db并回設(shè)到緩存了,這時(shí)候重試獲取緩存值即可
sleep(50);
get(key); //重試
}
} else {
return value;
}
}


2:"永遠(yuǎn)不過期": ?
這里的“永遠(yuǎn)不過期”包含兩層意思:

(1) 從redis上看,確實(shí)沒有設(shè)置過期時(shí)間,這就保證了,不會(huì)出現(xiàn)熱點(diǎn)key過期問題,也就是“物理”不過期。

(2) 從功能上看,如果不過期,那不就成靜態(tài)的了嗎?所以我們把過期時(shí)間存在key對(duì)應(yīng)的value里,如果發(fā)現(xiàn)要過期了,通過一個(gè)后臺(tái)的異步線程進(jìn)行緩存的構(gòu)建,也就是“邏輯”過期

? ? ? ? 從實(shí)戰(zhàn)看,這種方法對(duì)于性能非常友好,唯一不足的就是構(gòu)建緩存時(shí)候,其余線程(非構(gòu)建緩存的線程)可能訪問的是老數(shù)據(jù),但是對(duì)于一般的互聯(lián)網(wǎng)功能來說這個(gè)還是可以忍受。

String get(final String key) {
V v = redis.get(key);
String value = v.getValue();
long timeout = v.getTimeout();
if (v.timeout <= System.currentTimeMillis()) {
// 異步更新后臺(tái)異常執(zhí)行
threadPool.execute(new Runnable() {
public void run() {
String keyMutex = "mutex:" + key;
if (redis.setnx(keyMutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(keyMutex, 3 * 60);
String dbValue = db.get(key);
redis.set(key, dbValue);
redis.delete(keyMutex);
}
}
});
}
return value;
}

轉(zhuǎn)載于:https://www.cnblogs.com/zyy1688/p/10794989.html

總結(jié)

以上是生活随笔為你收集整理的缓存三大问题及解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。