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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

高并发下的缓存问题及布隆过滤器

發(fā)布時間:2024/3/12 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高并发下的缓存问题及布隆过滤器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一. 高并發(fā)下緩存的三大問題

1. 概述

  • 背景
  • 在高并發(fā)場景下,如果系統(tǒng)直連數(shù)據(jù)庫,數(shù)據(jù)庫會出現(xiàn)性能問題,甚至造成數(shù)據(jù)庫宕機,服務(wù)不可用。
  • 為了降低數(shù)據(jù)庫的壓力,我們通常會設(shè)計一個緩存系統(tǒng),在訪問數(shù)據(jù)庫之前,攔截一部分流量,保證系統(tǒng)的穩(wěn)定和數(shù)據(jù)庫的可用。
  • 高并發(fā)場景下緩存最常見的三大問題
    • 緩存雪崩
    • 緩存穿透
    • 緩存擊穿
  • 2. 緩存雪崩

    2.1 緩存雪崩的含義

  • 緩存雪崩:當某一個時刻出現(xiàn)大規(guī)模的緩存失效的情況,那么就會導(dǎo)致大量的請求直接打在數(shù)據(jù)庫上面,導(dǎo)致數(shù)據(jù)庫壓力巨大,如果在高并發(fā)的情況下,可能瞬間就會導(dǎo)致數(shù)據(jù)庫宕機。
  • 2.2 分析

  • 造成緩存雪崩的關(guān)鍵:在同一時間大規(guī)模的key失效。
  • 出現(xiàn)緩存雪崩的原因
  • 第一種:可能是Redis宕機
  • 第二種:可能是采用了相同的過期時間。
  • 2.3 緩存雪崩的解決方案

  • 法1:搭建Redis集群,防止Redis宕機導(dǎo)致緩存雪崩的問題,提高Redis的容災(zāi)性。
  • 法2:在原有的失效時間上加一個隨機值(比如1-5分鐘隨機),避免采用相同過期時間的key同時失效。
  • 法3:提高數(shù)據(jù)庫的容災(zāi)能力,可以使用分庫分表,讀寫分離的策略。
  • 法4:增加兜底措施(熔斷機制或服務(wù)降級),防止過多請求打到數(shù)據(jù)庫。

  • 3. 緩存擊穿

    3.1 緩存擊穿的含義

  • 緩存擊穿:一個熱點Key(數(shù)據(jù)庫存在該數(shù)據(jù)),有大并發(fā)集中對其進行訪問,突然間這個Key失效了,導(dǎo)致大并發(fā)全部打在數(shù)據(jù)庫上,導(dǎo)致數(shù)據(jù)庫壓力劇增,這種現(xiàn)象就叫做緩存擊穿。

  • 緩存雪崩 && 緩存擊穿

  • 緩存雪崩:大規(guī)模key同時失效;
  • 緩存擊穿:一個熱點key。
  • 3.2 分析

  • 關(guān)鍵:某個熱點的key失效,導(dǎo)致大并發(fā)集中打在數(shù)據(jù)庫上。
  • 解決方案的考慮
  • 第一種:熱點key不設(shè)置過期時間;
  • 第二種:降低打在數(shù)據(jù)庫的請求數(shù)量。
  • 3.3 緩存擊穿的解決方案

  • 法1:如果業(yè)務(wù)允許,將熱點key設(shè)置為永不過期;
  • 法2:使用互斥鎖,針對同一個key只允許一個線程到數(shù)據(jù)庫查詢。
    • 說明:如果緩存失效的情況,只有拿到鎖才可以查詢數(shù)據(jù)庫,降低了在同一時刻打在數(shù)據(jù)庫上的請求,防止數(shù)據(jù)庫打死。
    • 問題:導(dǎo)致系統(tǒng)的性能變差。

  • 4. 緩存穿透

    4.1 緩存穿透的含義

  • 緩存穿透:緩存和數(shù)據(jù)庫都沒有的數(shù)據(jù),被大量請求,這些請求像“穿透”了緩存一樣直接打在數(shù)據(jù)庫上,這種現(xiàn)象就叫做緩存穿透。
  • 4.2 分析

  • 關(guān)鍵:傳進來的key在緩存和數(shù)據(jù)庫均不存在。
  • 說明:假如有黑客傳進大量的不存在的key,則大量的請求打在數(shù)據(jù)庫上是很致命的,因此在日常開發(fā)中要對參數(shù)做好校驗,一些非法的參數(shù),不可能存在的key就直接返回錯誤提示,要對調(diào)用方保持這種“不信任”的心態(tài)。
  • 4.3 緩存穿透的解決方案

  • 法1:緩存空值/默認值,即把無效的Key存入緩存。
    • 詳解:如果Redis查不到數(shù)據(jù),數(shù)據(jù)庫也查不到,我們把這個Key存入緩存,設(shè)置其value=null,當下次再通過這個Key查詢時就不需要再查詢數(shù)據(jù)庫。
    • 缺點:如果傳進來的這個不存在的Key值每次都是隨機的,那存進Redis也沒有意義。
  • 法2:使用布隆過濾器。
    • 概述:布隆過濾器是一種概率性數(shù)據(jù)結(jié)構(gòu),它可以告訴我們數(shù)據(jù)一定不存在或可能存在。
    • 詳解:我們可以在緩存之前再加一層布隆過濾器,在查詢的時候先去布隆過濾器查詢key是否存在,如果不存在就直接返回。

  • 二. 布隆過濾器及其變體的介紹與應(yīng)用

    1. 布隆過濾器介紹

    1.1 概述

  • 布隆過濾器(Bloom Filter)是一種比較巧妙的、空間高效的、概率型數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)于1970年由布隆(Burton Howard Bloom)提出。
  • 其特點是高效插入和查詢,主要用于快速檢測一個元素是否在集合中,即告訴我們數(shù)據(jù)一定不存在或可能存在。
  • 1.2 基本思想

  • 數(shù)據(jù)結(jié)構(gòu):它由固定大小的二進制向量/位數(shù)組,以及一系列隨機映射函數(shù)(哈希函數(shù))兩部分組成。
  • 思想:其核心思想就是利用多個不同的Hash函數(shù)來解決沖突。
  • Hash碰撞:兩個不同元素映射到同一個哈希函數(shù)后得到的值可能相同。
  • 基本思想:它引入多個哈希函數(shù)來減少沖突。如果某一個哈希函數(shù)得出元素不在集合中,則該元素肯定不在集合中;只有所有哈希函數(shù)都判定該元素在集合中,才能確定該元素存在于集合中。
  • 1.3 原理

  • 位數(shù)組:布隆過濾器使用一個m比特的數(shù)組來保存信息。在初始狀態(tài)時,對于長度為m的位數(shù)組,它的所有位都被置為0。
  • Hash函數(shù):為了表達S={x1, x2,…,xn}這樣一個n個元素的集合,它使用k個相互獨立的哈希函數(shù),分別將集合中的每個元素映射到{1,…,m}的范圍中。
  • 添加元素到集合:當有變量被加入集合時,使用k個哈希函數(shù)得到k個哈希值,然后將數(shù)組中對應(yīng)的比特位設(shè)置為1(假定有2個元素,3個映射函數(shù))
    • 注意:如果一個位置多次被置為1,那么只有第一次會起作用,后面幾次將沒有任何效果。
  • 判斷元素是否存在:如判斷y是否屬于這個集合,只需要對y使用k個哈希函數(shù)得到k個哈希值,如果所有hash(y)的位置都是1,則布隆過濾器會認為y是集合中的元素,否則不在集合中。
  • 1.4 誤判率和特性

    思考:判斷兩個元素y1和y2。

    • y1:y1通過三個哈希函數(shù)的映射有一個位置為0,因此布隆過濾器會判斷y1不在集合中。
    • y2:y2通過三個哈希函數(shù)的映射所有位置均為1,因此布隆過濾器會判斷y2在集合中。但實際上,y2可能屬于這個集合,也可能不屬于,因為每個位置都可能與其他元素共用(即發(fā)生Hash沖突)。
  • 誤判率
    • 布隆過濾器的誤判:指多個輸入經(jīng)過哈希之后在相同的bit位置都為1,這樣就無法判斷究竟是哪個輸入產(chǎn)生的。
    • 誤判的概率:取決于Hash函數(shù)的個數(shù)、Hash函數(shù)沖撞的概率、位數(shù)組的大小。
      • 通常在工程里Hash函數(shù)用3~5個,實際值視需求而定。
    • 假陽性:把本來不存在布隆過濾器中的元素誤判為存在的情況叫做假陽性。
  • 特性
  • 從容器的角度
    • 如果布隆過濾器判斷元素在集合中存在,則不一定存在;
    • 如果布隆過濾器判斷不存在,則一定不存在。
  • 從元素的角度
    • 如果元素實際存在,則布隆過濾器一定判斷存在;
    • 如果元素實際不存在,則布隆過濾器可能判斷存在。
  • 1.5 優(yōu)缺點

  • 優(yōu)點:它的空間效率和查詢時間都遠遠超過一般的數(shù)據(jù)結(jié)構(gòu)(如傳統(tǒng)的List、Set、Map等數(shù)據(jù)結(jié)構(gòu))。
    - 空間效率和插入/查詢效率都是常數(shù)O(K)
    - 哈希函數(shù)相互獨立,利于硬件并行實現(xiàn)
    - 不需要存儲元素本身,數(shù)據(jù)安全
  • 缺點
    • 存在一定的誤判率;
    • 存放在布隆過濾器的數(shù)據(jù)不能刪除。
  • 1.6 適用場景和典型應(yīng)用

  • 適用場景:用于判定給定數(shù)據(jù)是否存在,但不嚴格要求100%正確的場合。
  • 典型應(yīng)用
  • 數(shù)據(jù)庫防止穿庫。Bigtable、HBase、Cassandra以及Postgresql使用布隆過濾器來減少不存在的行或列的磁盤查找,大幅度提高數(shù)據(jù)庫查詢的性能。
  • 判斷用戶是否閱讀過某視頻/文章。當然會導(dǎo)致一定的誤判,但不會讓用戶看到重復(fù)的內(nèi)容。
  • 緩存穿透場景。如果有一波冷數(shù)據(jù),在緩存前先通過布隆過濾器。如果布隆過濾器返回不存在,則直接返回;只有布隆過濾器返回存在,才去查詢緩存,如果沒查詢到,則到數(shù)據(jù)庫查詢。
  • WEB攔截器,如果相同請求則攔截,防止重復(fù)被攻擊。用戶第一次請求,將請求參數(shù)放入布隆過濾器中,當?shù)诙握埱髸r,先判斷請求參數(shù)是否被布隆過濾器命中,可以提高緩存命中率。Google Chrome瀏覽器使用了布隆過濾器加速安全瀏覽服務(wù)。
  • 布隆過濾器使用場景示意圖

  • 2. 布隆過濾器的實現(xiàn)

    2.1 布隆過濾器在Guava中的實現(xiàn)

  • 概述
  • 谷歌的Guava中提供了一個現(xiàn)成的布隆過濾器。
  • 占用內(nèi)存很小:如存儲100萬元素只占用0.87M的內(nèi)存,生成了5個哈希函數(shù)。
  • 說明
  • 布隆過濾器提供的存放元素的方法是put()
  • 布隆過濾器提供的判斷元素是否存在的方法是migjtContain()
  • 布隆過濾器的誤判率默認設(shè)置為0.03,也可以在創(chuàng)建時自行指定。
  • 位圖的容量是基于元素個數(shù)和誤判率計算出來的。
  • 根據(jù)位數(shù)組的大小,可以進一步計算出哈希函數(shù)的個數(shù)。
  • 實現(xiàn)示例
  • 引入依賴

    <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>25.0-jre</version> </dependency>
  • 實現(xiàn)示例

    public class bloomFilterTest {public static void main(String[] args) {// 創(chuàng)建布隆過濾器對象:創(chuàng)建了一個最多存放1500個字符串的布隆過濾器,并且誤判率為0.01BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.integerFunnel(),1500,0.01);// 將元素添加進布隆過濾器String data = "data01";bloomFilter.put(data);// 判斷指定元素是否存在System.out.println(filter.mightContain(data));} }
  • 說明
  • 當mightContain()方法返回true時,我們可以99%確定該元素在過濾器中;當過濾器返回false時,我們可以100%確定該元素不存在于過濾器中。
  • Guava提供的布隆過濾器實現(xiàn)算法比較權(quán)威,但只能在單機使用,而現(xiàn)在系統(tǒng)通常都是分布式場景。
  • 2.2 基于Tair的布隆過濾器

  • 概述:為了解決分布式場景的問題,集團的分布式緩存中間件Tair(Tair 2.0 RDB)也實現(xiàn)了BloomFilter。

  • 實現(xiàn)示例

  • 引入依賴<dependency><groupId>com.taobao.rdb</groupId><artifactId>rdb-client2</artifactId><version>2.5.0-hotkey</version> </dependency>
  • 實現(xiàn)代碼RdbSmartApi rdbSmartApi = RdbSmartFactory.getClientManager("xxx"); rdbSmartApi.setPassWord("xxx"); rdbSmartApi.init(); //Bloom過濾器的名字 String filterName = "myBloomFilter"; //預(yù)計要插入的數(shù)據(jù)大小 Long size = 10000000L; //誤判率,0.00001代表錯誤率為0.001% double errorRate = 0.00001; //創(chuàng)建一個BloomFilter String result = rdbSmartApi.sync().bfreserve(filterName.getBytes(),size,errorRate); //創(chuàng)建成功則返回OK if("OK".equals(result)){String data = "data01";//向BloomFilter中插入一條數(shù)據(jù)boolean success = rdbSmartApi.sync().bfadd(filterName.getBytes(),data.getBytes());if(success){//exist為是否存在boolean exist = rdbSmartApi.sync().bfexists(filterName.getBytes(),data.getBytes());} }
  • 2.3 基于Redis的布隆過濾器

  • 概述:Redis v4.0之后有了Module(模塊/插件)功能,Redis Modules讓Redis可以使用外部模塊擴展其功能,布隆過濾器就是其中的Module,官網(wǎng)推薦RedisBloom作為Redis布隆過濾器的Module。
    • Redis Modules的介紹:https://redis.io/modules
    • RedisBloom的介紹:https://github.com/RedisBloom/RedisBloom
  • 安裝RedisBloom
  • 法1:直接編輯進行安裝git clone https://github.com/RedisBloom/RedisBloom.git cd RedisBloom make #編譯 會生成一個rebloom.so文件 redis-server --loadmodule /path/to/rebloom.so #運行redis時加載布隆過濾器模塊 redis-cli # 啟動連接容器中的 redis 客戶端驗證
  • 法2:使用Docker進行安裝docker pull redislabs/rebloom:latest # 拉取鏡像 docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest #運行容器 docker exec -it redis-redisbloom bash redis-cli
  • 使用
  • 基本指令bf.add:添加元素到布隆過濾器 bf.exists:判斷元素是否在布隆過濾器 bf.madd:添加多個元素到布隆過濾器 bf.mexists:判斷多個元素是否在布隆過濾器 127.0.0.1:6379> bf.add user Tom (integer) 1 127.0.0.1:6379> bf.exists user Tom (integer) 1 127.0.0.1:6379> bf.exists user John (integer) 0 127.0.0.1:6379> bf.madd user Barry Jerry Mars 1) (integer) 1 2) (integer) 1 3) (integer) 1 127.0.0.1:6379> bf.mexists user Barry Linda 1) (integer) 1 2) (integer) 0
  • Redis的客戶端Redisson和lettuce基于布隆過濾器做了封裝。示例:Redisson<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.15.1</version> </dependency> public class RedissonBloomFilterDemo {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);// 布隆過濾器的名稱RBloomFilter<String> bloomFilter = redisson.getBloomFilter("user");// 初始化布隆過濾器,預(yù)計統(tǒng)計元素數(shù)量為55000000,期望誤判率為0.03bloomFilter.tryInit(55000000L, 0.03);bloomFilter.add("Tom");bloomFilter.add("Jack");System.out.println(bloomFilter.count()); //2System.out.println(bloomFilter.contains("Tom")); //trueSystem.out.println(bloomFilter.contains("Linda")); //false} }

  • 3. 布隆過濾器的變體

    3.1 CountingBloomFilter(計數(shù)布隆過濾器)

  • 背景:標準的布隆過濾器只支持插入和查找兩種操作,在集合是靜態(tài)集合時,它可以很好地工作。但是如果集合經(jīng)常變動,其弊端就顯現(xiàn)出來了,因為它不支持刪除操作。
  • 概述:CountingBloomFilter是BloomFilter的一個變種,它擴展標準布隆過濾器的數(shù)據(jù)結(jié)構(gòu),將底層數(shù)組的每一位擴展為一個4位大小的計數(shù)器Counter,用來存儲某個下標映射成功的頻次。它以占用更多的空間來換取支持刪除操作。
    • 插入元素時,通過k個哈希函數(shù)映射到k個計數(shù)器,這些命中的計數(shù)器值增加1;
    • 刪除元素時,刪除元素的時候,通過k個散列函數(shù)映射到k個計數(shù)器,這些計數(shù)器值減少1。
  • 使用CBF判斷元素是否在集合的規(guī)則
    • 某個元素通過k個散列函數(shù)映射到k個計數(shù)器,如果某個計數(shù)器的值為0,那么元素必定不在集合中;
    • 某個元素通過k個散列函數(shù)映射到k個計數(shù)器,如果所有計數(shù)器的值都大于0,那么元素可能在集合中。
  • 單機CountingBloomFilter的實現(xiàn)示例
  • 引入依賴<dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-common</artifactId><version>2.4.1</version> </dependency>
  • 實現(xiàn)代碼public class CountingBloomFilterDemo {public static void main(String[] args) {//預(yù)計數(shù)據(jù)的大小int dataSize = 100000000;//hash函數(shù)的個數(shù)int hashCount = 3;//使用的hash函數(shù)類型,0代表JenkinsHash,1代表MurmurHashint hashType = 1;//初始化一個CountingBloomFilterCountingBloomFilter filter = new CountingBloomFilter(dataSize,hashCount,hashType);//要put的數(shù)據(jù)String data = "data01";Key key = new Key(data.getBytes());//將data put到CountingBloomFilter中filter.add(key);//判斷data是否存在boolean exist = filter.membershipTest(key);//將data從CountingBloomFilter中刪除filter.delete(key);//判斷data是否存在filter.membershipTest(key);} }

  • 3.2 CuckooFilter(布谷鳥過濾器)

  • 概述
    • 解決標準BloomFilter無法刪除元素的問題;有更高的查詢效率;相比其他支持刪除的Filter更容易實現(xiàn)。
    • 它源于布谷鳥哈希算法。
  • (1)布谷鳥哈希算法
  • 數(shù)據(jù)結(jié)構(gòu):布谷鳥哈希表,它由一個桶數(shù)組組成,每個桶由一個數(shù)據(jù)項組成。
  • 原理
  • 每個插入項都有由哈希函數(shù)h1(x)和h2(x)確定的兩個候選桶,查找過程會檢查兩個桶是否任意一個桶包含此項。
  • 如果x的兩個桶中任何一個是空的,則算法將x插入到該空桶中,插入完成;
  • 如果兩個桶都沒有空間,項會選擇一個候選桶,踢出去現(xiàn)有的項,并將此被踢出項重新插入到它的備用位置。這個過程可能會重復(fù),直到找到一個空桶或達到最大位移次數(shù)。如果沒有找到空桶,則認為此哈希表太滿,則進行擴容和ReHash后,再次插入。
  • 雖然布谷鳥哈希可能執(zhí)行一系列重置,但其均攤插入時間為O(1)。
  • 說明

    • 圖(a):將新項x插入到8個桶的哈希表中的示例,其中x可以放置在桶2或6中。
    • 圖(b):表示待插入x的兩個桶都沒有空間,隨機選擇了候選桶6,踢出現(xiàn)有項a,重新放置“a”觸發(fā)了另一個重置,將現(xiàn)有的項“c”從桶4踢到桶1。
    (2)布谷鳥過濾器
  • 布谷鳥過濾器對布谷鳥哈希的改變

  • 為了提高桶的利用率,使用多路哈希桶。
  • 為了減少內(nèi)存的使用,只存儲元素的指紋信息。
  • 布谷鳥過濾器的數(shù)據(jù)結(jié)構(gòu):由一個哈希表組成,哈希表由一個桶數(shù)組組成,其中一個桶可以有多個條目,每個條目存儲一個指紋。

  • 哈希計算兩個候選桶索引的方案

    • 問題:布谷鳥過濾器只存儲指紋,因此無法恢復(fù)和重新哈希原始鍵以找到它們的替代位置;
    • 解決:利用一種稱為部分鍵布谷鳥哈希的技術(shù),來根據(jù)其指紋導(dǎo)出一個項的備用位置。
  • 插入

  • Algorithm 1: Insert(x) f = fingerprint(x); i1 = hash(x); i2 = i1 ⊕ hash(f); if bucket[i1] or bucket[i2] has an empty entry thenadd f to that bucket;return Done; // must relocate existing items; i = randomly pick i1 or i2; for n = 0; n < MaxNumKicks; n++ dorandomly select an entry e from bucket[i];swap f and the fingerprint stored in entry e;i = i ⊕ hash(f);if bucket[i] has an empty entry thenadd f to bucket[i];return Done; // Hashtable is considered full; return Failure;
  • 查找:給定一個項x,算法首先根據(jù)公式 (1)計算x的指紋和兩個候選桶。然后讀取這兩個桶:如果兩個桶中的任何現(xiàn)有指紋匹配,則布谷鳥過濾器返回true,否則過濾器返回false。
  • Algorithm 2: Lookup(x) f = fingerprint(x); i1 = hash(x); i2 = i1 ⊕ hash(f); if bucket[i1] or bucket[i2] has f thenreturn True; return False;
  • 刪除:它檢查給定項的兩個候選桶。如果任何桶中的指紋匹配,則從該桶中刪除匹配指紋的一份副本。
    • 注意:要安全地刪除項x,必須事先插入它,否則刪除非插入項可能會無意中刪除碰巧共享相同指紋的不同項。這一要求也適用于所有其他支持刪除的過濾器。
  • Algorithm 3: Delete(x) f = fingerprint(x); i1 = hash(x); i2 = i1 ⊕ hash(f); if bucket[i1] or bucket[i2] has f thenremove a copy of f from this bucket;return True; return False;
  • 說明:CuckooFilter不能擴容。因為我們已經(jīng)丟失了原值 x,則無法計算擴容后新的位置 hash(x)
  • (3)布谷鳥過濾器的實現(xiàn)
  • CockooFilter的Java單機版實現(xiàn)

  • 引入依賴<dependency><groupId>com.github.mgunlogson</groupId><artifactId>cuckoofilter4j</artifactId><version>1.0.2</version> </dependency>
  • 代碼實現(xiàn)public class CockooFilterDemo {public static void main(String[] args) {//要put的數(shù)據(jù)String data = "data01";//預(yù)計數(shù)據(jù)的大小long dataSize = 100000000L;//初始化一個CuckooFilterCuckooFilter<String> filter = new CuckooFilter.Builder<String> (Funnels.stringFunnel(Charset.defaultCharset()), dataSize).build();//將data put到CuckooFilter中filter.put(data);//判斷data是否存在boolean exist = filter.mightContain(data);//將data從CuckooFilter中移除filter.delete(data);} }
  • 基于Redis的CockooFilter:Redis的module的形式同樣支持CuckooFilter。和BloomFilter一樣,Jedis也沒有CuckooFilter的客戶端api調(diào)用實現(xiàn)。下面是redis官網(wǎng)對支持CuckooFilter的操作說明。

  • 基于Tair的CockooFilter:集團Tair RDB2.0支持高級數(shù)據(jù)結(jié)構(gòu)中,同樣包含CockooFilter。

  • 引入依賴<dependency><groupId>com.taobao.rdb</groupId><artifactId>rdb-client2</artifactId><version>2.5.0-hotkey</version> </dependency>
  • 代碼實現(xiàn)public class CockooFilterTairDemo {public static void main(String[] args) {RdbSmartApi rdbSmartApi = RdbSmartFactory.getClientManager("xxx");rdbSmartApi.setPassWord("xxx");rdbSmartApi.init();//Bloom過濾器的名字String filterName = "myBloomFilter";//預(yù)計要插入的數(shù)據(jù)大小Long size = 10000000L;//創(chuàng)建一個CuckooFilterString result = rdbSmartApi.sync().cfreserve(filterName.getBytes(),size);//創(chuàng)建成果,則返回OKif("OK".equals(result)){String data = "permission";//向BloomFilter中插入一條數(shù)據(jù)boolean success = rdbSmartApi.sync().bfadd(filterName.getBytes(),data.getBytes());if(success){//exist為是否存在,根據(jù)BloomFiler的特點,若exist為false,則代表該data一定不存在boolean exist = rdbSmartApi.sync().bfexists(filterName.getBytes(),data.getBytes());//將data從CuckFilter中刪除,這個歌功能BloomFilter是沒有的rdbSmartApi.sync().cfdel(filterName.getBytes(),data.getBytes());}}} }
  • 3.3 ScalableBloomFilter(可動態(tài)擴容的布隆過濾器)

  • 概述

    • TairBloom是云redis企業(yè)版(Tair3.0)上自帶的一個擴展數(shù)據(jù)結(jié)構(gòu),其采用redis module方式實現(xiàn)了一個可動態(tài)擴容的布隆過濾器(ScalableBloomFilter)。
    • TairBloom內(nèi)部本質(zhì)上是多層布隆過濾器(ScalableBloomFilter)的鏈式組合,只能執(zhí)行插入、查詢操作,無法執(zhí)行刪除操作。
    • 它解決了標準布隆過濾器容量受限、無法動態(tài)擴容的問題;具有動態(tài)擴容的能力,可在容量不足或者false positive無法保證時進行自動動態(tài)擴容,業(yè)務(wù)無需擔心容量問題;非常適合需要對大量數(shù)據(jù)進行高效去重的場景。
  • ScalableBloomFilter的思想:新建一個布隆過濾器,將多個布隆過濾器“組裝”成一個布隆過濾器使用。

  • ScalableBloomFilter的原理

  • 如圖展示的就是一個ScalableBloomFilter模型(下文簡稱SBF),該SBF一共包含BF0和BF1兩層。
  • 插入過程:只會向最后一層插入數(shù)據(jù)
  • 最開始時,SBF只包含BF0這一層,插入了a、b、c三個元素。
  • 這時,假設(shè)BF0已經(jīng)無法保證用戶設(shè)定的誤判率,此時就需要進行擴容,因此新的一層BF1被創(chuàng)建并加入進來。后來的d、e、f元素都會被插入到BF1中。
  • 同理,當BF1也無法滿足該層事先設(shè)定的誤判率時,新的一層BF2也將被加入進來,如此進行下去。
  • 查詢過程:由后向前
  • 查詢g這個元素是否在SBF中
  • 先在BF1中進行查詢。如果查詢顯示存在,則直接響應(yīng)客戶端;
  • 如果查詢顯示不存在,則繼續(xù)查詢BF0。如果BF0中顯示存在g,則響應(yīng)客戶端g存在。否則,因為BF0已經(jīng)是最后一層了,則響應(yīng)客戶端g不存在。
  • 說明:SBF的插入和查詢過程比較簡單。但是注意,首先,BF1在直觀上要比BF0長很多(m比特位數(shù)高);其次,BF1上的散列函數(shù)k(哈希函數(shù)個數(shù))也要比BF0上的大。

  • 應(yīng)用場景:非常適合推薦系統(tǒng)場景。文章閱讀功能,如果想留住用戶,就要盡可能給用戶推薦他喜歡類型的文章,但是又不能推薦重復(fù)的文章(特別是用戶最近剛讀過的文章)。為此,將用戶讀過的文章加入到TairBloom中,并在推薦給用戶之前,先去TairBloom中查詢一下該用戶是否讀過該文章,如果讀過就不推薦,否則就可以加入推薦列表。


  • 參考資料

    • 詳解布隆過濾器的原理,使用場景和注意事項
    • 布谷鳥過濾器:實際上優(yōu)于布隆過濾器
    • 布谷鳥哈希和布谷鳥過濾器
    • 冷飯新炒:理解布隆過濾器算法的實現(xiàn)原理
    • 緩存穿透、緩存擊穿、緩存雪崩,看這篇就夠了

    總結(jié)

    以上是生活随笔為你收集整理的高并发下的缓存问题及布隆过滤器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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