Redis處理高并發(fā)之布隆過濾器詳解
前言
隨著我們業(yè)務(wù)開發(fā)越來越來大,并染請(qǐng)求就會(huì)越來越多,那么我們的項(xiàng)目的壓力就會(huì)越來越大,基本都會(huì)使用緩存,除本地緩存,還會(huì)用到redis緩存,但是你以為使用緩存就沒啥問題了么,那肯定不是的,使用了緩存又會(huì)出現(xiàn)新的問題,比如,緩存的key失效導(dǎo)致大量的請(qǐng)求到數(shù)據(jù)庫,大量的讀請(qǐng)求瞬間到達(dá)了數(shù)據(jù)庫,cpu的使用率爆增,導(dǎo)致數(shù)據(jù)庫都可能掛掉,這種情況下我們就要考慮使用redis的布隆過濾器了。
緩存穿透、擊穿、雪崩
首先我們從緩存會(huì)出現(xiàn)的幾種問題,來進(jìn)行分析,在高并發(fā)的場(chǎng)景下如果出現(xiàn)這種情況,我們應(yīng)該如何解決。
正常情況下,我們的web應(yīng)用會(huì)先去請(qǐng)求緩存服務(wù),如果緩存命中,那么就去拿緩存里面的數(shù)據(jù),返回結(jié)果給應(yīng)用,

緩存穿透
緩存穿透與緩存雪崩和緩存擊穿還是不一樣的,雪崩和擊穿的情況下,數(shù)據(jù)庫的數(shù)據(jù)都是真正常的,可以去請(qǐng)求數(shù)據(jù)庫獲取數(shù)據(jù),只是緩存層出現(xiàn)問題,等待緩存恢復(fù)了,就會(huì)減輕數(shù)據(jù)庫的壓力。 而緩存透不一樣的就是,緩存和數(shù)據(jù)庫都沒有要請(qǐng)求的數(shù)據(jù),大量的請(qǐng)求來了,數(shù)據(jù)庫的壓力很大。

出現(xiàn)情況
- 數(shù)據(jù)庫數(shù)據(jù)被大量清除,導(dǎo)致訪問不到
- 黑客惡意攻擊
常見的解決方案
- redis緩存空值,請(qǐng)求不到的時(shí)候返回給應(yīng)用空值。
- 使用布隆過濾器,把數(shù)據(jù)庫的一部分?jǐn)?shù)據(jù)hash到布隆過濾器里,在請(qǐng)求數(shù)據(jù)庫之前先去布隆過濾器里篩選到一部分請(qǐng)求,判斷數(shù)據(jù)是否存在,避免直接去訪問數(shù)據(jù)庫。
緩存擊穿
出現(xiàn)情況
- 大量熱點(diǎn)數(shù)據(jù)庫過期,導(dǎo)致無法從緩存獲取到數(shù)據(jù),大量請(qǐng)求數(shù)據(jù)庫也無法返回書就

解決方案
- 加鎖,保證同一時(shí)間內(nèi),只允許有一個(gè)線程去更新緩存,等鎖釋放后在重新去請(qǐng)求緩存。
- 熱點(diǎn)數(shù)據(jù)不去設(shè)置過期時(shí)間,如果要設(shè)置過期時(shí)間,在過期的時(shí)候通知后臺(tái)去更新緩存的過期時(shí)間。
緩存雪崩
- 大量緩存在同一時(shí)間失效,導(dǎo)致大量請(qǐng)求進(jìn)入數(shù)據(jù)庫
- redis故障宕機(jī),導(dǎo)致緩存不能使用。
解決方案
- 同上加鎖
- 給緩存的過期時(shí)間加入隨機(jī)數(shù),保證緩存不會(huì)在同一時(shí)間同時(shí)失效。
- 副本key策略,就是對(duì)于一個(gè)key,在它的基礎(chǔ)上在設(shè)置一個(gè)key,它們的value都是一樣的,只不過一個(gè)設(shè)置過期時(shí)間、一個(gè)不設(shè)置過期時(shí)間,相當(dāng)于給key做了個(gè)副本,只不過在更新緩存的時(shí)候,副本key也是要更新的,避免出現(xiàn)數(shù)據(jù)不一致的現(xiàn)象。
布隆過濾器 Bloom filter
前面提到過布隆過濾器在請(qǐng)求比較高的時(shí)候,可以幫助我們抵擋一部分請(qǐng)求,從而減輕數(shù)據(jù)庫的壓力,布隆過濾器的數(shù)據(jù)結(jié)構(gòu)是一個(gè)二進(jìn)制的bit向量,或者說是一個(gè)bit數(shù)組,它相對(duì)于list、set、map這些集合,它占用的空間更少,不足之處處就是返回的結(jié)果會(huì)有一定概率的誤差。
public static void main(String[] args) {
int size = 1_000_000;
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);
for (int i = 0; i < size; i++) {
bloomFilter.put(i);
}
for (int i = 0; i < size; i++) {
if (!bloomFilter.mightContain(i)) {
System.out.println("有漏網(wǎng)之魚");
}
}
List<Integer> list = new ArrayList<>(1000);
for (int i = size + 10000; i < size + 20000; i++) {
if (bloomFilter.mightContain(i)) {
list.add(i);
}
}
System.out.println("有誤差的數(shù)量:" + list.size());
}
確實(shí)有誤差的數(shù)量,但是誤差量不大,追求效率的同時(shí)只是犧牲一點(diǎn)誤差了。

總結(jié)
加鎖的排隊(duì)的場(chǎng)景確實(shí)能幫助我們很好的解決緩存穿透、擊穿的一些問題,但是效率也是非常低了,因?yàn)槊總€(gè)請(qǐng)求都是排隊(duì)等待,如果可以接受輕微誤差的話,布隆過濾器的確是個(gè)很不錯(cuò)的選擇,Bloom filter的bitmap的存儲(chǔ)效率確實(shí)很高。
以上就是Redis處理高并發(fā)之布隆過濾器詳解的詳細(xì)內(nèi)容,更多關(guān)于Redis布隆過濾器處理高并發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Redis中key的命名規(guī)范和值的命名規(guī)范
這篇文章主要介紹了詳解Redis中key的命名規(guī)范和值的命名規(guī)范,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
基于Redis結(jié)合SpringBoot的秒殺案例詳解
這篇文章主要介紹了Redis結(jié)合SpringBoot的秒殺案例,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09

