欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis使用元素刪除的布隆過濾器來解決緩存穿透問題

 更新時間:2021年08月10日 09:55:38   作者:雙子孤狼  
本文主要介紹了Redis使用元素刪除的布隆過濾器來解決緩存穿透問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

在我們?nèi)粘i_發(fā)中,Redis使用場景最多的就是作為緩存和分布式鎖等功能來使用,而其用作緩存最大的目的就是為了降低數(shù)據(jù)庫訪問。但是假如我們某些數(shù)據(jù)并不存在于Redis當(dāng)中,那么請求還是會直接到達(dá)數(shù)據(jù)庫,而一旦在同一時間大量緩存失效或者一個不存在緩存的請求被惡意訪問,這些都會導(dǎo)致數(shù)據(jù)庫壓力驟增,這就是本文要講述的緩存穿透,緩存擊穿和緩存雪崩的問題,而布隆過濾器正是緩存穿透的一種解決方案。

緩存雪崩

緩存雪崩指的是Redis當(dāng)中的大量緩存在同一時間全部失效,而假如恰巧這一段時間同時又有大量請求被發(fā)起,那么就會造成請求直接訪問到數(shù)據(jù)庫,可能會把數(shù)據(jù)庫沖垮。

緩存雪崩一般形容的是緩存中沒有而數(shù)據(jù)庫中有的數(shù)據(jù),而因?yàn)闀r間到期導(dǎo)致請求直達(dá)數(shù)據(jù)庫。

解決方案

解決緩存雪崩的方法有很多:

1、加鎖,保證單線程訪問緩存。這樣就不會有很多請求同時訪問到數(shù)據(jù)庫。

2、失效時間不要設(shè)置成一樣。典型的就是初始化預(yù)熱數(shù)據(jù)的時候,將數(shù)據(jù)存入緩存時可以采用隨機(jī)時間來確保不會咋同一時間有大量緩存失效。

3、內(nèi)存允許的情況下,可以將緩存設(shè)置為永不失效。

緩存擊穿

緩存擊穿和緩存雪崩很類似,區(qū)別就是緩存擊穿一般指的是單個緩存失效,而同一時間又有很大的并發(fā)請求需要訪問這個key,從而造成了數(shù)據(jù)庫的壓力。

解決方案

解決緩存擊穿的方法和解決緩存雪崩的方法很類似:

1、加鎖,保證單線程訪問緩存。這樣第一個請求到達(dá)數(shù)據(jù)庫后就會重新寫入緩存,后續(xù)的請求就可以直接讀取緩存。2、內(nèi)存允許的情況下,可以將緩存設(shè)置為永不失效。

 緩存穿透

緩存穿透和上面兩種現(xiàn)象的本質(zhì)區(qū)別就是這時候訪問的數(shù)據(jù)其在數(shù)據(jù)庫中也不存在,那么既然數(shù)據(jù)庫不存在,所以緩存里面肯定也不會存在,這樣如果并發(fā)過大就會造成數(shù)據(jù)源源不斷的到達(dá)數(shù)據(jù)庫,給數(shù)據(jù)庫造成極大壓力。

解決方案

對于緩存穿透問題,加鎖并不能起到很好地效果,因?yàn)楸旧韐ey就是不存在,所以即使控制了線程的訪問數(shù),但是請求還是會源源不斷的到達(dá)數(shù)據(jù)庫。

解決緩存穿透問題一般可以采用以下方案配合使用:

1、接口層進(jìn)行校驗(yàn),發(fā)現(xiàn)非法的key直接返回。比如數(shù)據(jù)庫中采用的是自增id,那么如果來了一個非整型的id或者負(fù)數(shù)id可以直接返回,或者說如果采用的是32位uuid,那么發(fā)現(xiàn)id長度不等于32位也可以直接返回。

2、將不存在的數(shù)據(jù)也進(jìn)行緩存,可以直接緩存一個空或者其他約定好的無效value。采用這種方案最好將key設(shè)置一個短期失效時間,否則大量不存在的key被存儲到Redis中,也會占用大量內(nèi)存。

布隆過濾器(Bloom Filter)

針對上面緩存穿透的解決方案,我們思考一下:假如一個key可以繞過第1種方法的校驗(yàn),而此時有大量的不存在key被訪問(如1億個或者10億個),那么這時候全部存儲到緩存,會占用非常大的空間,會浪費(fèi)大量服務(wù)器內(nèi)存,導(dǎo)致內(nèi)存不足。

那么有沒有一種更好的解決方案呢?這就是我們接下來要介紹的布隆過濾器,布隆過濾器就可以最大程度的解決key值過多的這個問題。

什么是布隆過濾器

可能大部分人都知道有這么一個面試問題:如何在10億的海量的無序的數(shù)據(jù)中快速判斷一個元素是否存在?

要解決這個問題就需要用到布隆過濾器,否則大部分服務(wù)器的內(nèi)存是無法存儲這么大的數(shù)量級的數(shù)據(jù)的。

布隆過濾器(Bloom Filter)是由布隆在1970年提出的。它實(shí)際上是一個很長的二進(jìn)制向量(位圖)和一系列隨機(jī)映射函數(shù)(哈希函數(shù))。

布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優(yōu)點(diǎn)是空間效率和查詢時間都比一般的算法要好的多,缺點(diǎn)是有一定的誤識別率而且刪除困難。

位圖(Bitmap)

Redis當(dāng)中有一種數(shù)據(jù)結(jié)構(gòu)就是位圖,布隆過濾器其中重要的實(shí)現(xiàn)就是位圖的實(shí)現(xiàn),也就是位數(shù)組,并且在這個數(shù)組中每一個位置只有0和1兩種狀態(tài),每個位置只占用1個比特(bit),其中0表示沒有元素存在,1表示有元素存在。如下圖所示就是一個簡單的布隆過濾器示例(一個key值經(jīng)過哈希運(yùn)算和位運(yùn)算就可以得出應(yīng)該落在哪個位置):

在這里插入圖片描述

哈希碰撞

上面我們發(fā)現(xiàn),lonelywolf落在了同一個位置,這種不同的key值經(jīng)過哈希運(yùn)算后得到相同值的現(xiàn)象就稱之為哈希碰撞。發(fā)生哈希碰撞之后再經(jīng)過位運(yùn)算,那么最后肯定會落在同一個位置。

如果發(fā)生過多的哈希碰撞,就會影響到判斷的準(zhǔn)確性,所以為了減少哈希碰撞,我們一般會綜合考慮以下2個因素:

1、增大位圖數(shù)組的大?。ㄎ粓D數(shù)組越大,占用的內(nèi)存越大)。

2、增加哈希函數(shù)的次數(shù)(同一個key值經(jīng)過1個函數(shù)相等了,那么經(jīng)過2個或者更多個哈希函數(shù)的計(jì)算,都得到相等結(jié)果的概率就自然會降低了)。

上面兩個方法我們需要綜合考慮:比如增大位數(shù)組,那么就需要消耗更多的空間,而經(jīng)過越多的哈希計(jì)算也會消耗cpu影響到最終的計(jì)算時間,所以位數(shù)組到底多大,哈希函數(shù)次數(shù)又到底需要計(jì)算多少次合適需要具體情況具體分析。

布隆過濾器的2大特點(diǎn)

下面這個就是一個經(jīng)過了2次哈希函數(shù)得到的布隆過濾器,根據(jù)下圖我們很容易看到,假如我們的Redis根本不存在,但是Redis經(jīng)過2次哈希函數(shù)之后得到的兩個位置已經(jīng)是1了(一個是wolf通過f2得到,一個是Nosql通過f1得到)。

在這里插入圖片描述

所以通過上面的現(xiàn)象,我們從布隆過濾器的角度可以得出布隆過濾器主要有2大特點(diǎn):

1、如果布隆過濾器判斷一個元素存在,那么這個元素可能存在。

2、如果布隆過濾器判斷一個元素不存在,那么這個元素一定不存在。

而從元素的角度也可以得出2大特點(diǎn):

1、如果元素實(shí)際存在,那么布隆過濾器一定會判斷存在。

2、如果元素不存在,那么布隆過濾器可能會判斷存在

PS:需要注意的是,如果經(jīng)過N次哈希函數(shù),則需要得到的N個位置都是1才能判定存在,只要有一個是0,就可以判定為元素不存在布隆過濾器中。

fpp

因?yàn)椴悸∵^濾器中總是會存在誤判率,因?yàn)楣E鲎彩遣豢赡馨俜职俦苊獾摹?strong>布隆過濾器對這種誤判率稱之為假陽性概率,即:False Positive Probability,簡稱為fpp。

在實(shí)踐中使用布隆過濾器時可以自己定義一個fpp,然后就可以根據(jù)布隆過濾器的理論計(jì)算出需要多少個哈希函數(shù)和多大的位數(shù)組空間。需要注意的是這個fpp不能定義為100%,因?yàn)闊o法百分保證不發(fā)生哈希碰撞。

布隆過濾器的實(shí)現(xiàn)(Guava)

在Guava的包中提供了布隆過濾器的實(shí)現(xiàn),下面就通過Guava來體會一下布隆過濾器的應(yīng)用:
1、引入pom依賴

<dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>29.0-jre</version>
</dependency>

2、新建一個布隆過濾器的測試demo:

package com.lonelyWolf.redis;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BloomFilterDemo {
    private static final int expectedInsertions = 1000000;

    public static void main(String[] args) {
        BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),expectedInsertions);

        List<String> list = new ArrayList<>(expectedInsertions);


        for (int i = 0; i < expectedInsertions; i++) {
            String uuid = UUID.randomUUID().toString();
            bloomFilter.put(uuid);
            list.add(uuid);
        }

        int rightNum1 = 0;
        int wrongNum1 = 0;

        NumberFormat percentFormat =NumberFormat.getPercentInstance();
        percentFormat.setMaximumFractionDigits(2); //最大小數(shù)位數(shù)

        for (int i=0;i < 500;i++){
            String key = list.get(i);
            if (bloomFilter.mightContain(key)){
                if (list.contains(key)){
                    rightNum1++;
                }else {
                    wrongNum1++;
                }
            }
        }
        System.out.println("布隆過濾器認(rèn)為存在的key值數(shù):" + rightNum1);
        System.out.println("-----------------------分割線---------------------------------");

        int rightNum2 = 0;
        int wrongNum2 = 0;

        for (int i=0;i < 5000;i++){
            String key = UUID.randomUUID().toString();
            if (bloomFilter.mightContain(key)){
                if (list.contains(key)){
                    rightNum2++;
                }else {
                    wrongNum2++;
                }
            }
        }

        System.out.println("布隆過濾器認(rèn)為存在的key值數(shù):" + rightNum2);
        System.out.println("布隆過濾器認(rèn)為不存在的key值數(shù):" + wrongNum2);
        System.out.println("布隆過濾器的誤判率為:" + percentFormat.format((float)wrongNum2 / 5000));
    }
}

運(yùn)行之后,第一部分輸出的值一定是和for循環(huán)內(nèi)的值相等,也就是百分百匹配,即滿足了原則1:如果元素實(shí)際存在,那么布隆過濾器一定會判斷存在。
第二部分的輸出的誤判率即fpp總是在3%左右,而且隨著for循環(huán)的次數(shù)越大,越接近3%。即滿足了原則2:如果元素不存在,那么布隆過濾器可能會判斷存在。

這個3%的誤判率是如何來的呢?我們進(jìn)入創(chuàng)建布隆過濾器的create方法,發(fā)現(xiàn)默認(rèn)的fpp就是0.03:

在這里插入圖片描述

對于這個默認(rèn)的3%的fpp需要多大的位數(shù)組空間和多少次哈希函數(shù)得到的呢?在BloomFilter類下面有兩個default方法可以獲取到位數(shù)組空間大小和哈希函數(shù)的個數(shù):

  • optimalNumOfHashFunctions:獲取哈希函數(shù)的次數(shù)
  • optimalNumOfBits:獲取位數(shù)組大小

debug進(jìn)去看一下:

在這里插入圖片描述

得到的結(jié)果是7298440 bit=0.87M,然后經(jīng)過了5次哈希運(yùn)算??梢园l(fā)現(xiàn)這個空間占用是非常小的,100W的key才占用了0.87M。

PS:點(diǎn)擊這里可以進(jìn)入網(wǎng)站計(jì)算bit數(shù)組大小和哈希函數(shù)個數(shù)。

布隆過濾器的如何刪除

上面的布隆過濾器我們知道,判斷一個元素存在就是判斷對應(yīng)位置是否為1來確定的,但是如果要刪除掉一個元素是不能直接把1改成0的,因?yàn)檫@個位置可能存在其他元素,所以如果要支持刪除,那我們應(yīng)該怎么做呢?最簡單的做法就是加一個計(jì)數(shù)器,就是說位數(shù)組的每個位如果不存在就是0,存在幾個元素就存具體的數(shù)字,而不僅僅只是存1,那么這就有一個問題,本來存1就是一位就可以滿足了,但是如果要存具體的數(shù)字比如說2,那就需要2位了,所以帶有計(jì)數(shù)器的布隆過濾器會占用更大的空間。

帶有計(jì)數(shù)器的布隆過濾器

下面就是一個帶有計(jì)數(shù)器的布隆過濾器示例
1、引入依賴:

<dependency>
    <groupId>com.baqend</groupId>
    <artifactId>bloom-filter</artifactId>
    <version>1.0.7</version>
</dependency>

2、新建一個帶有計(jì)數(shù)器的布隆過濾器demo:

package com.lonelyWolf.redis.bloom;

import orestes.bloomfilter.FilterBuilder;

public class CountingBloomFilter {
    public static void main(String[] args) {
        orestes.bloomfilter.CountingBloomFilter<String> cbf = new FilterBuilder(10000,
                0.01).countingBits(8).buildCountingBloomFilter();

        cbf.add("zhangsan");
        cbf.add("lisi");
        cbf.add("wangwu");
        System.out.println("是否存在王五:" + cbf.contains("wangwu")); //true
        cbf.remove("wangwu");
        System.out.println("是否存在王五:" + cbf.contains("wangwu")); //false
    }
}

構(gòu)建布隆過濾器前面2個參數(shù)一個就是期望的元素?cái)?shù),一個就是fpp值,后面的countingBits參數(shù)就是計(jì)數(shù)器占用的大小,這里傳了一個8位,即最多允許255次重復(fù),如果不傳的話這里默認(rèn)是16位大小,即允許65535次重復(fù)。

總結(jié)

本文主要講述了使用Redis存在的三種問題:緩存雪崩緩存擊穿緩存穿透。并分別對每種問題的解決方案進(jìn)行了描述,最后著重介紹了緩存穿透的解決方案:布隆過濾器。原生的布隆過濾器不支持刪除,但是可以引入一個計(jì)數(shù)器實(shí)現(xiàn)帶有計(jì)數(shù)器的布隆過濾器來實(shí)現(xiàn)刪除功能,同時在最后也提到了,帶有計(jì)數(shù)器的布隆過濾器會占用更多的空間問題。

到此這篇關(guān)于Redis使用元素刪除的布隆過濾器來解決緩存穿透問題的文章就介紹到這了,更多相關(guān)Redis 緩存穿透內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一篇文章帶你徹底搞懂Redis?事務(wù)

    一篇文章帶你徹底搞懂Redis?事務(wù)

    這篇文章主要介紹了一篇文章帶你徹底搞懂Redis?事務(wù)的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • 一篇文章帶你弄清楚Redis的精髓

    一篇文章帶你弄清楚Redis的精髓

    Redis是一個開源的、支持網(wǎng)絡(luò)、基于內(nèi)存的鍵值對存儲系統(tǒng),它可以用作數(shù)據(jù)庫、緩存和消息中間件。它支持多種數(shù)據(jù)類型,包括字符串、散列、列表、集合、位圖等,擁有極快的讀寫速度,并且支持豐富的特性,如事務(wù)、持久化、復(fù)制、腳本、發(fā)布/訂閱等。
    2023-02-02
  • Redis?抽獎大轉(zhuǎn)盤的實(shí)戰(zhàn)示例

    Redis?抽獎大轉(zhuǎn)盤的實(shí)戰(zhàn)示例

    本文主要介紹了Redis?抽獎大轉(zhuǎn)盤的實(shí)戰(zhàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 詳解Redis中的List是如何實(shí)現(xiàn)的

    詳解Redis中的List是如何實(shí)現(xiàn)的

    List 的 Redis 中的 5 種主要數(shù)據(jù)結(jié)構(gòu)之一,它是一種序列集合,可以存儲一個有序的字符串列表,順序是插入的順序,本文將給大家介紹了一下Redis中的List是如何實(shí)現(xiàn)的,需要的朋友可以參考下
    2024-05-05
  • redis哨兵模式說明與搭建詳解

    redis哨兵模式說明與搭建詳解

    這篇文章主要介紹了redis哨兵模式說明與搭建詳解,需要的朋友可以參考下
    2023-01-01
  • Redis中一個String類型引發(fā)的慘案

    Redis中一個String類型引發(fā)的慘案

    著存儲的數(shù)據(jù)量越來越大,Redis的內(nèi)存的使用量也快速上升,結(jié)果遇到了大內(nèi)存Redis實(shí)例因?yàn)樯蒖DB而響應(yīng)變慢的問題。很顯然String類型并不是一種好的選擇,那有什么辦法可以降低內(nèi)存消耗嗎?帶著這個問題一起通過本文學(xué)習(xí)下吧
    2021-07-07
  • Redis實(shí)現(xiàn)分布式鎖詳解

    Redis實(shí)現(xiàn)分布式鎖詳解

    這篇文章主要介紹了redis如何實(shí)現(xiàn)分布式鎖,文章中有詳細(xì)的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2023-04-04
  • Redis緩存三大異常的處理方案梳理總結(jié)

    Redis緩存三大異常的處理方案梳理總結(jié)

    這篇文章主要介紹了Redis緩存三大異常的處理方案梳理總結(jié),緩存方式,在提高數(shù)據(jù)查詢效率、保護(hù)數(shù)據(jù)庫等方面起到了不可磨滅的作用,但實(shí)際應(yīng)用中,可能會出現(xiàn)一些Redis緩存異常的情況,下文對其方案總結(jié)需要的朋友可以參考一下
    2022-06-06
  • Redis設(shè)置Hash數(shù)據(jù)類型的過期時間

    Redis設(shè)置Hash數(shù)據(jù)類型的過期時間

    在Redis中,我們可以使用Hash數(shù)據(jù)結(jié)構(gòu)來存儲一組鍵值對,而有時候,我們可能需要設(shè)置這些鍵值對的過期時間,本文主要介紹了Redis設(shè)置Hash數(shù)據(jù)類型的過期時間,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • redis 解決庫存并發(fā)問題實(shí)現(xiàn)數(shù)量控制

    redis 解決庫存并發(fā)問題實(shí)現(xiàn)數(shù)量控制

    本文主要介紹了redis 解決庫存并發(fā)問題實(shí)現(xiàn)數(shù)量控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04

最新評論