大家好,我是哪吒。
今天分享一下redis布隆過(guò)濾器的原理和應(yīng)用場(chǎng)景,解決緩存穿透,實(shí)現(xiàn)快速入門,豐富個(gè)人簡(jiǎn)歷,提高面試level,給自己增加一點(diǎn)談資,秒變面試小達(dá)人,BAT不是夢(mèng)。
一、緩存預(yù)熱
Redis緩存預(yù)熱是指在服務(wù)器啟動(dòng)或應(yīng)用程序啟動(dòng)之前,將一些數(shù)據(jù)先存儲(chǔ)到Redis中,以提高Redis的性能和數(shù)據(jù)一致性。這可以減少服務(wù)器在啟動(dòng)或應(yīng)用程序啟動(dòng)時(shí)的數(shù)據(jù)傳輸量和延遲,從而提高應(yīng)用程序的性能和可靠性。
1、緩存預(yù)熱常見(jiàn)步驟
(1)數(shù)據(jù)準(zhǔn)備
在應(yīng)用程序啟動(dòng)或服務(wù)器啟動(dòng)之前,準(zhǔn)備一些數(shù)據(jù),這些數(shù)據(jù)可以是靜態(tài)數(shù)據(jù)、緩存數(shù)據(jù)或其他需要預(yù)熱的數(shù)據(jù)。
(2)數(shù)據(jù)存儲(chǔ)
將數(shù)據(jù)存儲(chǔ)到Redis中,可以使用Redis的列表(List)數(shù)據(jù)類型或集合(Set)數(shù)據(jù)類型。
(3)數(shù)據(jù)預(yù)熱
在服務(wù)器啟動(dòng)或應(yīng)用程序啟動(dòng)之前,將數(shù)據(jù)存儲(chǔ)到Redis中??梢允褂肦edis的客戶端工具或命令行工具來(lái)執(zhí)行此操作。
(4)數(shù)據(jù)清洗
在服務(wù)器啟動(dòng)或應(yīng)用程序啟動(dòng)之后,可能會(huì)對(duì)存儲(chǔ)在Redis中的數(shù)據(jù)進(jìn)行清洗和處理。例如,可以刪除過(guò)期的數(shù)據(jù)、修改錯(cuò)誤的數(shù)據(jù)等。
需要注意的是,Redis緩存預(yù)熱可能會(huì)增加服務(wù)器的開(kāi)銷,因此應(yīng)該在必要時(shí)進(jìn)行。同時(shí),為了減少預(yù)熱的次數(shù),可以考慮使用Redis的其他數(shù)據(jù)類型,如哈希表(Hash)或有序集合(Sorted Set)。此外,為了提高數(shù)據(jù)一致性和性能,可以使用Redis的持久化功能,將數(shù)據(jù)存儲(chǔ)到Redis中,并在服務(wù)器重啟后自動(dòng)恢復(fù)數(shù)據(jù)。
2、代碼實(shí)現(xiàn)
@Component
@Slf4j
public class BloomFilterInit
{
@Resource
private RedisTemplate redisTemplate;
//初始化白名單數(shù)據(jù)
@PostConstruct
public void init() {
//1 白名單客戶加載到布隆過(guò)濾器
String key = "customer:1";
//2 計(jì)算hashValue,由于存在計(jì)算出來(lái)負(fù)數(shù)的可能,我們?nèi)〗^對(duì)值
int hashValue = Math.abs(key.hashCode());
//3 通過(guò)hashValue和2的32次方后取余,獲得對(duì)應(yīng)的下標(biāo)坑位
long index = (long)(hashValue % Math.pow(2,32));
log.info(key+" 對(duì)應(yīng)的坑位index:{}",index);
//4 設(shè)置redis里面的bitmap對(duì)應(yīng)類型白名單:whitelistCustomer的坑位,將該值設(shè)置為1
redisTemplate.opsForValue().setBit("whitelistCustomer",index,true);
}
}
二、緩存雪崩
Redis緩存雪崩是指在緩存系統(tǒng)中,由于某些原因,緩存的數(shù)據(jù)突然大量地被刪除或修改,導(dǎo)致緩存系統(tǒng)的性能下降,甚至無(wú)法正常工作。
1、什么情況會(huì)發(fā)生緩存雪崩?
(1)誤刪除
由于誤操作或故障,緩存系統(tǒng)可能會(huì)誤刪除一些正常的數(shù)據(jù)。這種情況通常會(huì)在數(shù)據(jù)庫(kù)中發(fā)生。
(2)誤修改
由于誤操作或故障,緩存系統(tǒng)可能會(huì)誤修改一些正常的數(shù)據(jù)。這種情況通常會(huì)在數(shù)據(jù)庫(kù)中發(fā)生。
(3)負(fù)載波動(dòng)
緩存系統(tǒng)通常會(huì)承受一定的負(fù)載波動(dòng),例如,在高峰期間,數(shù)據(jù)量可能會(huì)大幅增加,從而導(dǎo)致緩存系統(tǒng)的性能下降。
(4)數(shù)據(jù)變化頻繁
如果緩存系統(tǒng)中的數(shù)據(jù)變化頻繁,例如,每秒鐘都會(huì)有大量的數(shù)據(jù)插入或刪除,那么緩存系統(tǒng)可能會(huì)因?yàn)轫憫?yīng)過(guò)慢而導(dǎo)致雪崩。
2、Redis緩存集群實(shí)現(xiàn)高可用
- 主從 + 哨兵
- Redis集群
- 開(kāi)啟Redis持久化機(jī)制aof/rdb,盡快恢復(fù)緩存集群。
3、如何避免Redis緩存雪崩?
(1)數(shù)據(jù)備份
定期備份數(shù)據(jù),以防止誤刪除或誤修改。
(2)數(shù)據(jù)同步
定期同步數(shù)據(jù),以防止數(shù)據(jù)不一致。
(3)負(fù)載均衡
使用負(fù)載均衡器將請(qǐng)求分配到多個(gè)Redis實(shí)例上,以減輕單個(gè)實(shí)例的負(fù)載。
(4)數(shù)據(jù)優(yōu)化
優(yōu)化數(shù)據(jù)庫(kù)結(jié)構(gòu),減少數(shù)據(jù)變化頻繁的情況。
(5)監(jiān)控與告警
監(jiān)控Redis實(shí)例的性能指標(biāo),及時(shí)發(fā)現(xiàn)緩存系統(tǒng)的異常,并發(fā)出告警。
三、緩存穿透
Redis緩存穿透是指在Redis緩存系統(tǒng)中,由于某些原因,緩存的數(shù)據(jù)無(wú)法被正常訪問(wèn)或處理,導(dǎo)致緩存失去了它的作用。
1、什么情況會(huì)發(fā)生緩存穿透?
(1)數(shù)據(jù)量過(guò)大
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)量過(guò)大時(shí),緩存的數(shù)據(jù)量可能會(huì)超過(guò)Redis的數(shù)據(jù)存儲(chǔ)限制,從而導(dǎo)致緩存失去了它的作用。
(2)數(shù)據(jù)更新頻繁
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)更新頻繁時(shí),緩存的數(shù)據(jù)可能會(huì)出現(xiàn)異步的變化,導(dǎo)致緩存無(wú)法被正常訪問(wèn)。
(3)數(shù)據(jù)過(guò)期
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)過(guò)期時(shí),緩存的數(shù)據(jù)可能會(huì)失去它的作用,因?yàn)镽edis會(huì)在一定時(shí)間后自動(dòng)將過(guò)期的數(shù)據(jù)刪除。
(4)數(shù)據(jù)權(quán)限限制
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)受到權(quán)限限制時(shí),只有擁有足夠權(quán)限的用戶才能訪問(wèn)和處理這些數(shù)據(jù),從而導(dǎo)致緩存失去了它的作用。
(5)Redis性能瓶頸
當(dāng)Redis服務(wù)器的性能達(dá)到極限時(shí),Redis緩存可能會(huì)因?yàn)轫憫?yīng)過(guò)慢而導(dǎo)致穿透。
2、如何避免Redis緩存穿透?
(1)設(shè)置合理的緩存大小
根據(jù)實(shí)際需求設(shè)置合理的緩存大小,以避免緩存穿透。
(2)優(yōu)化數(shù)據(jù)結(jié)構(gòu)
根據(jù)實(shí)際需求優(yōu)化數(shù)據(jù)結(jié)構(gòu),以減少數(shù)據(jù)的大小和更新頻率。
(3)設(shè)置合理的過(guò)期時(shí)間
設(shè)置合理的過(guò)期時(shí)間,以避免緩存失去它的作用。
(4)增加Redis的并發(fā)處理能力
通過(guò)增加Redis的并發(fā)處理能力,以提高緩存的處理能力和響應(yīng)速度。
(5)優(yōu)化Redis服務(wù)器的硬件和軟件配置
通過(guò)優(yōu)化Redis服務(wù)器的硬件和軟件配置,以提高Redis的性能和處理能力。
Redis緩存穿透
四、通過(guò)空對(duì)象緩存解決緩存穿透
如果發(fā)生了緩存穿透,可以針對(duì)要查詢的數(shù)據(jù),在Redis中插入一條數(shù)據(jù),添加一個(gè)約定好的默認(rèn)值,比如defaultNull。
比如你想通過(guò)某個(gè)id查詢某某訂單,Redis中沒(méi)有,MySQL中也沒(méi)有,此時(shí),就可以在Redis中插入一條,存為defaultNull,下次再查詢就有了,因?yàn)槭翘崆凹s定好的,前端也明白是啥意思,一切OK,歲月靜好。
這種方式只能解決key相同的情況,如果key都不同,則完蛋。
五、google布隆過(guò)濾器Guava解決緩存穿透
1、引入pom
<!--guava Google 開(kāi)源的 Guava 中自帶的布隆過(guò)濾器-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
2、創(chuàng)建布隆過(guò)濾器
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 100);
布隆過(guò)濾器中添加元素。
bloomFilter.mightContain(1)
判斷布隆過(guò)濾器中是否存在。
bloomFilter.mightContain(1)
3、fpp誤判率
@Service
@Slf4j
public class GuavaBloomFilterService {
public static final int SIZE = 1000000;
//誤判率
public static double fpp = 0.01;
//創(chuàng)建guava布隆過(guò)濾器
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), SIZE, fpp);
public void guavaBloomFilter() {
for (int i = 1; i <= SIZE; i++) {
bloomFilter.put(i);
}
ArrayList<Integer> list = new ArrayList<>(10000);
for (int i = SIZE + 1; i <= SIZE + (10000); i++) {
if (bloomFilter.mightContain(i)) {
log.info("被誤判了:{}", i);
list.add(i);
}
}
log.info("誤判總數(shù)量:{}", list.size());
}
}
六、Redis緩存擊穿
Redis緩存擊穿是指在Redis緩存系統(tǒng)中,由于某些原因,緩存的數(shù)據(jù)無(wú)法被正常訪問(wèn)或處理,導(dǎo)致緩存失去了它的作用。
1、什么情況會(huì)發(fā)生緩存擊穿?
根本原因:熱點(diǎn)Key失效。
(1)數(shù)據(jù)量過(guò)大
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)量過(guò)大時(shí),緩存的數(shù)據(jù)量可能會(huì)超過(guò)Redis的數(shù)據(jù)存儲(chǔ)限制,從而導(dǎo)致緩存失去了它的作用。
(2)數(shù)據(jù)更新頻繁
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)更新頻繁時(shí),緩存的數(shù)據(jù)可能會(huì)出現(xiàn)異步的變化,導(dǎo)致緩存無(wú)法被正常訪問(wèn)。
(3)數(shù)據(jù)過(guò)期
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)過(guò)期時(shí),緩存的數(shù)據(jù)可能會(huì)失去它的作用,因?yàn)镽edis會(huì)在一定時(shí)間后自動(dòng)將過(guò)期的數(shù)據(jù)刪除。
(4)數(shù)據(jù)權(quán)限限制
當(dāng)緩存中存儲(chǔ)的數(shù)據(jù)受到權(quán)限限制時(shí),只有擁有足夠權(quán)限的用戶才能訪問(wèn)和處理這些數(shù)據(jù),從而導(dǎo)致緩存失去了它的作用。
(5)Redis性能瓶頸
當(dāng)Redis服務(wù)器的性能達(dá)到極限時(shí),Redis緩存可能會(huì)因?yàn)轫憫?yīng)過(guò)慢而導(dǎo)致?lián)舸?/p>
2、如何避免Redis緩存擊穿?
(1)設(shè)置合理的緩存大小
根據(jù)實(shí)際需求設(shè)置合理的緩存大小,以避免緩存穿透。
(2)優(yōu)化數(shù)據(jù)結(jié)構(gòu)
根據(jù)實(shí)際需求優(yōu)化數(shù)據(jù)結(jié)構(gòu),以減少數(shù)據(jù)的大小和更新頻率。
(3)設(shè)置合理的過(guò)期時(shí)間
設(shè)置合理的過(guò)期時(shí)間,以避免緩存失去它的作用。
(4)增加Redis的并發(fā)處理能力
通過(guò)增加Redis的并發(fā)處理能力,以提高緩存的處理能力和響應(yīng)速度。
(5)優(yōu)化Redis服務(wù)器的硬件和軟件配置
通過(guò)優(yōu)化Redis服務(wù)器的硬件和軟件配置,以提高Redis的性能和處理能力。
七、Redis緩存擊穿解決方案
1、互斥更新
通過(guò)雙檢加鎖機(jī)制。
2、差異失效時(shí)間
先更新從緩存B,再更新主緩存A,而且讓從緩存B的緩存失效時(shí)間長(zhǎng)于A,保證A失效時(shí),B還在。
本文轉(zhuǎn)載自微信公眾號(hào)「哪吒編程」