[轉]硬核 | Redis 布隆(Bloom Filter)過濾器原理與實戰

在Redis 緩存擊穿(失效)、緩存穿透、緩存雪崩怎么解決?中我們說到可以使用布隆過濾器避免「緩存穿透」。

碼哥,布隆過濾器還能在哪些場景使用呀?

比如我們使用「碼哥跳動」開發的「明日頭條」APP 看新聞,如何做到每次推薦給該用戶的內容不會重復,過濾已經看過的內容呢?

你會說我們只要記錄了每個用戶看過的歷史記錄,每次推薦的時候去查詢數據庫過濾存在的數據實現去重

實際上,如果歷史記錄存儲在關系數據庫里,去重就需要頻繁地對數據庫進行 exists 查詢,當系統并發量很高時,數據庫是很難扛住壓力的。

碼哥,我可以使用緩存啊,把歷史數據存在 Redis 中。

萬萬不可,這么多的歷史記錄那要浪費多大的內存空間,所以這個時候我們就能使用布隆過濾器去解決這種去重問題。又快又省內存,互聯網開發必備殺招!

當你遇到數據量大,又需要去重的時候就可以考慮布隆過濾器,如下場景:

  • 解決 Redis 緩存穿透問題(面試重點);

  • 郵件過濾,使用布隆過濾器實現郵件黑名單過濾;

  • 爬蟲爬過的網站過濾,爬過的網站不再爬取;

  • 推薦過的新聞不再推薦;

什么是布隆過濾器

布隆過濾器 (Bloom Filter)是由 Burton Howard Bloom 于 1970 年提出,它是一種 space efficient 的概率型數據結構,用于判斷一個元素是否在集合中

當布隆過濾器說,某個數據存在時,這個數據可能不存在;當布隆過濾器說,某個數據不存在時,那么這個數據一定不存在。

哈希表也能用于判斷元素是否在集合中,但是布隆過濾器只需要哈希表的 1/8 或 1/4 的空間復雜度就能完成同樣的問題。

布隆過濾器可以插入元素,但不可以刪除已有元素。

其中的元素越多,false positive rate(誤報率)越大,但是 false negative (漏報)是不可能的。

布隆過濾器原理

BloomFilter 的算法是,首先分配一塊內存空間做 bit 數組,數組的 bit 位初始值全部設為 0。

加入元素時,采用 k 個相互獨立的 Hash 函數計算,然后將元素 Hash 映射的 K 個位置全部設置為 1。

檢測 key 是否存在,仍然用這 k 個 Hash 函數計算出 k 個位置,如果位置全部為 1,則表明 key 存在,否則不存在。

如下圖所示:

2c54b91d6dfa42f10b28caa75c36d363.png
布隆過濾器原理

哈希函數會出現碰撞,所以布隆過濾器會存在誤判。

這里的誤判率是指,BloomFilter 判斷某個 key 存在,但它實際不存在的概率,因為它存的是 key 的 Hash 值,而非 key 的值。

所以有概率存在這樣的 key,它們內容不同,但多次 Hash 后的 Hash 值都相同。

對于 BloomFilter 判斷不存在的 key ,則是 100% 不存在的,反證法,如果這個 key 存在,那它每次 Hash 后對應的 Hash 值位置肯定是 1,而不會是 0。布隆過濾器判斷存在不一定真的存在。

碼哥,為什么不允許刪除元素呢?

刪除意味著需要將對應的 k 個 bits 位置設置為 0,其中有可能是其他元素對應的位。

因此 remove 會引入 false negative,這是絕對不被允許的。

Redis 集成布隆過濾器

Redis 4.0 的時候官方提供了插件機制,布隆過濾器正式登場。以下網站可以下載官方提供的已經編譯好的可拓展模塊。

https://redis.com/redis-enterprise-software/download-center/modules/

73faa0a977dc84e6b294e79385d8a5c3.png

RedisModules

碼哥推薦使用 Redis 版本 6.x,最低 4.x 來集成布隆過濾器。如下指令查看版本,碼哥安裝的版本是 6.2.6。

redis-server?-v
Redis?server?v=6.2.6?sha=00000000:0?malloc=libc?bits=64?build=b5524b65e12bbef5

下載

我們自己編譯安裝,需要從 github 下載,目前的 release 版本是 v2.2.14,下載地址:https://github.com/RedisBloom/RedisBloom/releases/tag/v2.2.14

3e94be41b1915f7ba2f3f873b94829a1.png

Redis 布隆

解壓編譯

解壓

tar?-zxvf?RedisBloom-2.2.14.tar

編譯插件

cd?RedisBloom-2.2.14
make

編譯成功,會看到 redisbloom.so 文件。

安裝集成

需要修改 redis.conf 文件,新增 loadmodule配置,并重啟 Redis。

loadmodule?/opt/app/RedisBloom-2.2.14/redisbloom.so

如果是集群,則每個實例的配置文件都需要加入配置。

4ede4e4faaf6b6649a6695fb858e1123.png

module 配置

指定配置文件并啟動 Redis:

redis-server /opt/app/redis-6.2.6/redis.conf

加載成功的頁面如下:

d998cf0cf657e4f22b8b041d952d3235.png

加載布隆過濾器成功

客戶端連接 Redis 測試。

BF.ADD?--添加一個元素到布隆過濾器
BF.EXISTS?--判斷元素是否在布隆過濾器
BF.MADD?--添加多個元素到布隆過濾器
BF.MEXISTS?--判斷多個元素是否在布隆過濾器

a5bf0fb7dd3df1d4fc0447bfddda60df.png

測試

Redis 布隆過濾器實戰

我們來用布隆過濾器來解決緩存穿透問題,緩存穿透:意味著有特殊請求在查詢一個不存在的數據,即數據不存在 Redis 也不存在于數據庫。

當用戶購買商品創建訂單的時候,我們往 mq 發送消息,把訂單 ID 添加到布隆過濾器。

18af062498f7b2f3a45cb9f1a413f979.png

訂單同步到布隆過濾器

在添加到布隆過濾器之前,我們通過BF.RESERVE命令手動創建一個名字為 orders error_rate = 0.1 ,初始容量為 10000000 的布隆過濾器:

#?BF.RESERVE?{key}?{error_rate}?{capacity}?[EXPANSION?{expansion}]?[NONSCALING]
BF.RESERVE?orders?0.1?10000000
  • key:filter 的名字;

  • error_rate:期望的錯誤率,默認 0.1,值越低,需要的空間越大;

  • capacity:初始容量,默認 100,當實際元素的數量超過這個初始化容量時,誤判率上升。

  • EXPANSION:可選參數,當添加到布隆過濾器中的數據達到初始容量后,布隆過濾器會自動創建一個子過濾器,子過濾器的大小是上一個過濾器大小乘以 expansion;expansion 的默認值是 2,也就是說布隆過濾器擴容默認是 2 倍擴容;

  • NONSCALING:可選參數,設置此項后,當添加到布隆過濾器中的數據達到初始容量后,不會擴容過濾器,并且會拋出異常((error) ERR non scaling filter is full) 說明:BloomFilter 的擴容是通過增加 BloomFilter 的層數來完成的。每增加一層,在查詢的時候就可能會遍歷多層 BloomFilter 來完成,每一層的容量都是上一層的兩倍(默認)。

如果不使用BF.RESERVE命令創建,而是使用 Redis 自動創建的布隆過濾器,默認的 error_rate0.01capacity是 100。

布隆過濾器的 error_rate 越小,需要的存儲空間就越大,對于不需要過于精確的場景,error_rate 設置稍大一點也可以。

布隆過濾器的 capacity 設置的過大,會浪費存儲空間,設置的過小,就會影響準確率,所以在使用之前一定要盡可能地精確估計好元素數量,還需要加上一定的冗余空間以避免實際元素可能會意外高出設置值很多。

添加訂單 ID 到過濾器

#?BF.ADD?{key}?{item}
BF.ADD?orders?10086
(integer)?1

使用 BF.ADD向名稱為 orders 的布隆過濾器添加 10086 這個元素。

如果是多個元素同時添加,則使用 BF.MADD key {item ...},如下:

BF.MADD?orders?10087?10089
1)?(integer)?1
2)?(integer)?1

判斷訂單是否存在

#?BF.EXISTS?{key}?{item}
BF.EXISTS?orders?10086
(integer)?1

BF.EXISTS 判斷一個元素是否存在于BloomFilter,返回值 = 1 表示存在。

如果需要批量檢查多個元素是否存在于布隆過濾器則使用 BF.MEXISTS,返回值是一個數組:

  • 1:存在;

  • 0:不存在。

#?BF.MEXISTS?{key}?{item}
BF.MEXISTS?orders?100?10089
1)?(integer)?0
2)?(integer)?1

總體說,我們通過BF.RESERVE、BF.ADD、BF.EXISTS三個指令就能實現避免緩存穿透問題。

碼哥,如何查看創建的布隆過濾器信息呢?

BF.INFO key查看,如下:

BF.INFO?orders1)?Capacity2)?(integer)?100000003)?Size4)?(integer)?77941845)?Number?of?filters6)?(integer)?17)?Number?of?items?inserted8)?(integer)?39)?Expansion?rate
10)?(integer)?2

返回值:

  • Capacity:預設容量;

  • Size:實際占用情況,但如何計算待進一步確認;

  • Number of filters:過濾器層數;

  • Number of items inserted:已經實際插入的元素數量;

  • Expansion rate:子過濾器擴容系數(默認 2);

碼哥,如何刪除布隆過濾器呢?

目前布隆過濾器不支持刪除,布谷過濾器Cuckoo Filter是支持刪除的。

Bloom 過濾器在插入項目時通常表現出更好的性能和可伸縮性(因此,如果您經常向數據集添加項目,那么 Bloom 過濾器可能是理想的)。布谷鳥過濾器在檢查操作上更快,也允許刪除。

大家有興趣可可以看下:https://oss.redis.com/redisbloom/Cuckoo_Commands/)

碼哥,我想知道你是如何掌握這么多技術呢?

其實我也是翻閱官方文檔并做一些簡單加工而已,這篇的文章內容實戰就是基于 Redis 官方文檔上面的例子:https://oss.redis.com/redisbloom/。

大家遇到問題一定要耐心的從官方文檔尋找答案,培養自己的閱讀和定位問題的能力。

Redission 布隆過濾器實戰

碼哥的樣例代碼基于 Spring Boot 2.1.4,代碼地址:https://github.com/MageByte-Zero/springboot-parent-pom。

添加 Redission 依賴:

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.7</version>
</dependency>

使用 Spring boot 默認的 Redis 配置方式配置 Redission:

spring:application:name:?redissionredis:host:?127.0.0.1port:?6379ssl:?false

創建布隆過濾器

@Service
public?class?BloomFilterService?{@Autowiredprivate?RedissonClient?redissonClient;/***?創建布隆過濾器*?@param?filterName?過濾器名稱*?@param?expectedInsertions?預測插入數量*?@param?falseProbability?誤判率*?@param?<T>*?@return*/public?<T>?RBloomFilter<T>?create(String?filterName,?long?expectedInsertions,?double?falseProbability)?{RBloomFilter<T>?bloomFilter?=?redissonClient.getBloomFilter(filterName);bloomFilter.tryInit(expectedInsertions,?falseProbability);return?bloomFilter;}}

單元測試

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes?=?RedissionApplication.class)
public?class?BloomFilterTest?{@Autowiredprivate?BloomFilterService?bloomFilterService;@Testpublic?void?testBloomFilter()?{//?預期插入數量long?expectedInsertions?=?10000L;//?錯誤比率double?falseProbability?=?0.01;RBloomFilter<Long>?bloomFilter?=?bloomFilterService.create("ipBlackList",?expectedInsertions,?falseProbability);//?布隆過濾器增加元素for?(long?i?=?0;?i?<?expectedInsertions;?i++)?{bloomFilter.add(i);}long?elementCount?=?bloomFilter.count();log.info("elementCount?=?{}.",?elementCount);//?統計誤判次數int?count?=?0;for?(long?i?=?expectedInsertions;?i?<?expectedInsertions?*?2;?i++)?{if?(bloomFilter.contains(i))?{count++;}}log.info("誤判次數?=?{}.",?count);bloomFilter.delete();}
}

注意事項:如果是 Redis Cluster 集群,則需要 RClusteredBloomFilter<SomeObject> bloomFilter = redisson.getClusteredBloomFilter("sample");
?

參考資料

1.https://blog.csdn.net/u010066934/article/details/122026625?

2.https://juejin.cn/book/6844733724618129422/section/6844733724706209806?

3.https://www.cnblogs.com/heihaozi/p/12174478.html?

4.https://www.cnblogs.com/allensun/archive/2011/02/16/1956532.html?

5.https://oss.redis.com/redisbloom/Bloom_Commands/?

6.https://oss.redis.com/redisbloom/?

7.https://redis.com/blog/rebloom-bloom-filter-datatype-redis

e236f680c0c67107925e148a4f5c0533.png

作者?| 碼哥字節

出品?|?碼哥字節

---------------------
作者:無雙.
來源:CSDN
原文:https://blog.csdn.net/sinat_32849897/article/details/123700560
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284334.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284334.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284334.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Senparc.Weixin.Sample.MP源碼剖析

Senparc.Weixin.Sample.MP是微信公眾號樣例的.NET6源碼&#xff0c;項目配置文件appsettings.json的修改和微信公眾號測試環境的搭建參考&#xff1a;微信公眾號調試與Natapp環境搭建。接下來從項目結構&#xff0c;項目應用和項目源碼3個角度來進行講解。一.項目結構角度項目代…

解決java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor問題

hibernate整合spring當在spring配置文件中加入如下代碼 <!--2.配置事務屬性,需要事務管理器--><tx:advice id"txAdvice" transaction-manager"transactionManager"><tx:attributes><tx:method name"get*" read-only"t…

Packagist / Composer 中國全量鏡像

Packagist / Composer中國全量鏡像 本鏡像共緩存了 186695 個項目(package)、Millions 個(zip)安裝包。最后同步時間&#xff1a;2018/1/28 上午11:01:13 。Composer 最新版本&#xff1a;1.6.2 立即使用 贊助 Packagist 鏡像使用方法 還沒安裝 Composer 嗎&#xff1f;請往…

mock.js使用

一、Mock.js入門 1&#xff0e; 什么是mock.js? Mock.js &#xff08;官網http://mockjs.com/&#xff09;是一款模擬數據生成器&#xff0c;旨在幫助前端攻城獅獨立 于后端進行開發&#xff0c;幫助編寫單元測試。提供了以下模擬功能&#xff1a; 1,根據數據模板生成模擬數據…

面向對象——概念(成員變量、靜態變量、成員方法、靜態方法、垃圾回收機制、重載、包)...

靜態變量和成員變量的區別&#xff1a; 1、成員變量描述的是對象的特征&#xff0c;包含在對象之中。不同的對象成員變量彼此獨立。一個對象成員變量的改變&#xff0c;不會影響其他對象。 靜態變量獨立在對象之外&#xff0c;是所有對象共享的變量。靜態變量改變后會影響所有對…

【ArcGIS微課1000例】0042:ArcGIS自帶取色器工具的妙用

在ArcGIS中作圖時,通常要進行顏色對照填充,輸入特定的RGB值,本文介紹ArcGIS自帶取色器工具的妙用,及第三方顏色拾取工具。 文章目錄 一、ArcGIS自帶取色器二、第三方取色器工具一、ArcGIS自帶取色器 很多人可能不知道,ArcGIS中自帶取色器工具,如下圖所示。 當然了,自帶…

第一輪復習完畢,kmp走起

//代碼via:http://blog.csdn.net/v_JULY_v/article/details/6111565 //簡單思路via:http://study.163.com/course/courseLearn.htm?courseId468002#/learn/video?lessonId1024414&courseId468002 1 #include<iostream>2 #include<string>3 #include<vecto…

微信.NET SDK-Senparc資料整理

微信生態系統包括微信公眾號、小程序、微信支付、微信開放平臺、企業微信、小游戲等&#xff0c;官方提供了很多的API接口。Senparc是目前使用最廣泛的微信.NET SDK&#xff0c;同時支持支持.NET Framework 4.5/.NET Core 2.x/.NET Core 3.x/.NET 5/.NET 6。由于在微信生態開發…

7 種提升 Spring Boot 吞吐量神技

目錄 二、增加內嵌Tomcat的最大連接數 三、使用ComponentScan()定位掃包比SpringBootApplication掃包更快 四、默認tomcat容器改為Undertow&#xff08;Jboss下的服務器&#xff0c;Tomcat吞吐量5000&#xff0c;Undertow吞吐量8000&#xff09; 五、使用 BufferedWriter 進…

Atitit.ati?orm的設計and架構總結?適用于java?c#?php版

Atitit.ati orm的設計and架構總結 適用于java c# php版 1. Orm的目標 1 1.1. 動態obj 1 1.2. Hb的api(meger,save,update,del) 1 2. Orm的概念 1 3. 動態obj 2 4. 參考 4 1. Orm的目標 1.1. 動態obj 1.2. Hb的api(meger,save,update,del) 2. Orm的概念 saveOrUpdate后的對象會納…

【ArcGIS微課1000例】0043:ArcGIS縮略圖的創建及應用

縮略圖通常出現在地圖文檔中&#xff0c;便于在啟動頁面中快速打開指定的地圖文檔&#xff0c;提高效率。 文章目錄一、縮略圖預覽二、縮略圖創建一、縮略圖預覽 打開ArcMap軟件&#xff0c;彈出啟動窗口&#xff0c;在最近打開的文檔中&#xff0c;可以看到兩類&#xff0c;一…

JSP簡單登錄系統

Login登陸界面 <body> 登陸 <% session.invalidate();%> <form action"TestPW.jsp" method"post">用戶名<input type"text" name"username"> 密碼<input type"password" name"password&quo…

手動從0搭建ABP框架-ABP官方完整解決方案和手動搭建簡化解決方案實踐

本文主要講解了如何把ABP官方的在線生成解決方案運行起來&#xff0c;并說明了解決方案中項目間的依賴關系。然后手動實踐了如何從0搭建了一個簡化的解決方案。ABP官方的在線生成解決方案源碼下載參考[3]&#xff0c;手動搭建的簡化的解決方案源碼下載參考[4]。一.ABP官方在線生…

Java捕獲并處理線程失敗拋出的異常

使用 UncaughtExceptionHandler 示例代碼如下&#xff1a; Thread.UncaughtExceptionHandler handler new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) {System.out.println("Uncaught exception: " ex);} }; Th…

【ArcGIS微課1000例】0044:ArcGIS使用山體陰影顯示DEM的3種方法

本文講解了ArcGIS使用山體陰影顯示DEM的3種方法:“影像分析”窗口、使用山體陰影效果和山體陰影效果工具的不同之處。 文章目錄 一、“影像分析”窗口二、使用山體陰影效果三、山體陰影工具一、“影像分析”窗口 使用山體陰影顯示 DEM 的方法有兩種。最簡單并且最具交互效果的…

區塊鏈每日投資指南(0129)-證監會副主席表示數字貨幣需要監管

上一周的走勢依然是工作日下跌&#xff0c;周末拉升的結局。這主要原因依然是&#xff0c;周末不上班。最終政策出爐之前&#xff0c;市場恐怕還將繼續震蕩。下周的工作日恐怕會重演下跌的節奏。但是經過了17號&#xff0c;23號&#xff0c;26號三次筑底來看&#xff0c;如果政…

藍綠發布、滾動發布、灰度發布,有什么區別?

在項目迭代的過程中&#xff0c;不可避免需要”上線“。上線對應著部署&#xff0c;或者重新部署&#xff1b;部署對應著修改&#xff1b;修改則意味著風險。目前有很多部署發布的技術, 這兒將常見的做一個總結。 上面所說難免有些抽象, 舉一個情景例子, 加入你是微博項目負責…

iOS 音頻開發

音頻基礎知識 組成 音頻文件的組成&#xff1a;文件格式(或者音頻容器) 數據格式(或者音頻編碼)。 文件格式(或音頻容器)是用于形容文件本身的格式。 我們可以通過多種不同的方法為真正的音頻數據編碼。例如CAF文件便是一種文件格式&#xff0c;它能夠包含MP3格式&#xff0c;…

【ArcGIS微課1000例】0045:ArcGIS制圖模板的自定義與使用方法

怎樣在ArcGIS中保存地圖模板以在地圖制圖與打印之前使用呢? 文章目錄 一、地圖模板簡介二、地圖模板創建1. 創建模板2. 創建縮略圖3. 保存模板三、地圖模板使用一、地圖模板簡介 使用ArcMap打開一個已有的地圖模板,【文件】→【新建】,任選一個模板,這里選擇一個傳統模板。…

api 接口開發理論 在php中調用接口以及編寫接口

如&#xff1a;http://localhost/openUser.php?actget_user_list&typejson 在這里openUser.php相當于一個接口&#xff0c;其中get_user_list 是一個API&#xff08;獲取用戶列表&#xff09;&#xff0c;講求返回的數據類型為JSON格式。 你只需要在你PHP代碼中執行這條鏈…