《玩轉Redis》系列文章主要講述Redis的基礎及中高級應用。本文是《玩轉Redis》系列第【11】篇,最新系列文章請前往公眾號“zxiaofan”查看,或百度搜索“玩轉Redis zxiaofan”即可。
往期精選:《玩轉Redis-HyperLogLog原理探索》
本文關鍵字:玩轉Redis、Bloom filter、布隆過濾器、無偏hash函數;
大綱
- 布隆過濾器介紹
- 什么是布隆過濾器
- 布隆過濾器有什么特性
- Redis布隆過濾器實戰
- rebloom的安裝
- 布隆過濾器的命令詳解及示例
- 布隆過濾器的底層原理
- 布隆過濾器的底層結構
- 最佳hash函數數量與錯誤率的關系
- 所需存儲空間與錯誤率及容量關系
- 布隆過濾器如何擴容
- 布隆過濾器有哪些應用場景
- 布隆過濾器的優缺點
- 延伸拓展
1、布隆過濾器介紹
先前我們學習了HyperLogLog(傳送門《玩轉Redis-HyperLogLog原理探索》《玩轉Redis-HyperLogLog統計微博日活月活》),非常適合大數據下的基數計算場景,但其有個缺陷,無法判斷某個值是否已存在。
Hash、Set、String的BitMap等可以實現判斷元素是否存在的功能,但這些實現方式要么隨着元素增多會占用大量內存(Hash、Set),要么無法動態伸縮和保持誤判率不變(BitMap)。因此,我們非常需要一種可以高效判斷大量數據是否存在且允許一定誤判率的數據結構。
1.1、什么是布隆過濾器(Bloom Filter)
布隆過濾器由Burton Howard Bloom於1970年提出,用於判斷一個元素是否在集合中。
布隆過濾器(Bloom filter)是一種非常節省空間的概率數據結構(space-efficient probabilistic data structure),運行速度快(時間效率),占用內存小(空間效率),但是有一定的誤判率且無法刪除元素。本質上由一個很長的二進制向量和一系列隨機映射函數組成。
1.2 布隆過濾器有什么特性
- 檢查一個元素是否在集成中;
- 檢查結果分為2種:一定不在集合中、可能在集合中;
- 布隆過濾器支持添加元素、檢查元素,但是不支持刪除元素;
- 檢查結果的“可能在集合中”說明存在一定誤判率;
- 已經添加進入布隆過濾器的元素是不會被誤判的,僅未添加過的元素才可能被誤判;
- 相比set、Bitmaps非常節省空間:因為只存儲了指紋信息,沒有存儲元素本身;
- 添加的元素超過預設容量越多,誤報的可能性越大。
2、Redis布隆過濾器實戰
2.1、rebloom的安裝
還沒有安裝Redis的同學,可以參考我先前的文章安裝,傳送門《玩轉Redis-Redis安裝、后台啟動、卸載》。Redis 4.0開始以插件形式提供布隆過濾器。
# docker方式安裝 > docker pull redislabs/rebloom # 拉取鏡像 > docker run -p6379:6379 redislabs/rebloom # 運行容器 > redis-cli # 連接容器中的 redis 服務
# linux服務器直接安裝 >git clone git://github.com/RedisLabsModules/rebloom >cd rebloom >make # 當前路徑會生成一個rebloom.so文件 # 在redis的配置中(通常在/etc/redis/redis.conf)增加一行配置 loadmodule /"rebloom.so的絕對路徑"/rebloom.so # 重啟Redis即可
上述的安裝提到需要重啟Redis,但是生產環境的Redis可不是你想重啟就重啟的。有什么方式可以不重啟Redis就加載rebloom插件嗎,MODULE LOAD命令就派上用場了。
# 不重啟Redis加載rebloom插件 1、查看redis當前已加載的插件 > MODULE LOAD /"rebloom.so的絕對路徑"/redisbloom.so > module list 1) 1) "name" 2) "bf" 3) "ver" 4) (integer) 999999 # 看到以上數據則說明redisbloom加載成功了,模塊名name為"bf",模塊版本號ver為999999。 # 動態執行模塊卸載 # MODULE UNLOAD 模塊名 # 當然,為了防止Redis重啟導致動態加載的模塊丟失,我們還是應該在redis.conf 中加上相關配置。
2.2、布隆過濾器的命令詳解及示例
完整指令說明可前往官網查看:https://oss.redislabs.com/redisbloom/Bloom_Commands/。
2.2.1、Bloom命令簡述
【核心命令】添加元素:BF.ADD(添加單個)、BF.MADD(添加多個)、BF.INSERT(添加多個);
【核心命令】檢查元素是否存在:BF.EXISTS(查詢單個元素)、BF.MEXISTS(查詢多個元素)
命令 | 功能 | 參數 |
---|---|---|
BF.RESERVE | 創建一個大小為capacity,錯誤率為error_rate的空的Bloom | BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] [NONSCALING] |
BF.ADD | 向key指定的Bloom中添加一個元素item | BF.ADD {key} {item} |
BF.MADD | 向key指定的Bloom中添加多個元素 | BF.MADD {key} {item} [item...] |
BF.INSERT | 向key指定的Bloom中添加多個元素,添加時可以指定大小和錯誤率,且可以控制在Bloom不存在的時候是否自動創建 | BF.INSERT {key} [CAPACITY {cap}] [ERROR {error}] [EXPANSION expansion] [NOCREATE] [NONSCALING] ITEMS {item...} |
BF.EXISTS | 檢查一個元素是否可能存在於key指定的Bloom中 | BF.EXISTS {key} {item} |
BF.MEXISTS | 同時檢查多個元素是否可能存在於key指定的Bloom中 | BF.MEXISTS {key} {item} [item...] |
BF.SCANDUMP | 對Bloom進行增量持久化操作 | BF.SCANDUMP {key} {iter} |
BF.LOADCHUNK | 加載SCANDUMP持久化的Bloom數據 | BF.LOADCHUNK {key} {iter} {data} |
BF.INFO | 查詢key指定的Bloom的信息 | BF.INFO {key} |
BF.DEBUG | 查看BloomFilter的內部詳細信息(如每層的元素個數、錯誤率等) | BF.DEBUG {key} |
2.2.2、BF.RESERVE
- 參數
- BF.RESERVE {key} {error_rate} {capacity}
- 功能
- 創建一個大小為capacity,錯誤率為error_rate的空的BloomFilter
- 時間復雜度
- O(1)
- 參數說明
- key:布隆過濾器的key;
- error_rate:期望的錯誤率(False Positive Rate),該值必須介於0和1之間。該值越小,BloomFilter的內存占用量越大,CPU使用率越高。
- capacity:布隆過濾器的初始容量,即期望添加到布隆過濾器中的元素的個數。當實際添加的元素個數超過該值時,布隆過濾器將進行自動的擴容,該過程會導致性能有所下降,下降的程度是隨着元素個數的指數級增長而線性下降。
- 可選參數
- expansion:當添加到布隆過濾器中的數據達到初始容量后,布隆過濾器會自動創建一個子過濾器,子過濾器的大小是上一個過濾器大小乘以expansion。expansion的默認值是2,也就是說布隆過濾器擴容默認是2倍擴容。
- NONSCALING:設置此項后,當添加到布隆過濾器中的數據達到初始容量后,不會擴容過濾器,並且會拋出異常((error) ERR non scaling filter is full)。
- 返回值
- 成功:OK;
- 其它情況返回相應的異常信息。
- 備注
- BloomFilter的擴容是通過增加BloomFilter的層數來完成的。每增加一層,在查詢的時候就可能會遍歷多層BloomFilter來完成,每一層的容量都是上一層的兩倍(默認)。
# 公眾號@zxiaofan # 創建一個容量為5且不允許擴容的過濾器; 127.0.0.1:6379> bf.reserve bf2 0.1 5 NONSCALING OK 127.0.0.1:6379> bf.madd bf2 1 2 3 4 5 1) (integer) 1 2) (integer) 1 3) (integer) 1 4) (integer) 1 5) (integer) 1 # 添加第6個元素時即提示BloomFilter已滿; 127.0.0.1:6379> bf.madd bf2 6 1) (error) ERR non scaling filter is full 127.0.0.1:6379> bf.info bf2 1) Capacity 2) (integer) 5 3) Size 4) (integer) 155 5) Number of filters 6) (integer) 1 7) Number of items inserted 8) (integer) 5 9) Expansion rate 10) (integer) 2
2.2.3、BF.ADD
- 參數
- BF.ADD {key} {item}
- 功能
- 向key指定的Bloom中添加一個元素item。
- 時間復雜度
- O(log N),N是過濾器的層數。
- 參數說明
- key:布隆過濾器的名字;
- item:待插入過濾器的元素;
- 返回值
- 元素不存在插入成功:返回1;
- 元素可能已經存在:返回0;
- 其它情況返回相應的異常信息。
2.2.3、BF.MADD
- 參數
- BF.MADD {key} {item} [item...]
- 功能
- 向key指定的Bloom中添加多個元素item。
- 時間復雜度
- O(log N),N是過濾器的層數。
- 參數說明
- key:布隆過濾器的名字;
- item:待插入過濾器的元素,可插入多個;
- 返回值
- 成功:返回一個數組,數組的每一個元素可能為1或0,當item一定不存在時數組元素值為1,當item可能已經存在時數組元素值為0。
- 其它情況返回相應的異常信息。
2.2.5、BF.EXISTS
- 參數
- BF.EXISTS {key} {item}
- 功能
- 檢查一個元素是否可能存在於key指定的Bloom中
- 時間復雜度
- O(log N),N是過濾器的層數。
- 參數說明
- key:布隆過濾器的名字;
- item:待檢查的元素;
- 返回值
- 元素一定不存在:0;
- 元素可能存在:1;
- 其它情況返回相應的異常信息。
2.2.6、BF.MEXISTS
- 參數
- BF.MEXISTS <key> <item> [item...]
- 功能
- 檢查多個元素是否可能存在於key指定的Bloom中
- 時間復雜度
- O(log N),N是過濾器的層數。
- 參數說明
- key:布隆過濾器的名字;
- item:待檢查的元素,可設置多個;
- 返回值
- 成功:返回一個數組,數組的每一個元素可能為1或0,當item一定不存在時數組元素值為0,當item可能已經存在時數組元素值為1。
- 其它情況返回相應的異常信息。
# 公眾號@zxiaofan # 向BloomFilter添加單個元素 127.0.0.1:6379> bf.add bf1 itemadd1 (integer) 1 # 向BloomFilter批量添加多個元素 127.0.0.1:6379> bf.madd bf1 itemmadd1 itemmadd2 1) (integer) 1 2) (integer) 1 127.0.0.1:6379> bf.exists itemmadd1 (error) ERR wrong number of arguments for 'bf.exists' command 127.0.0.1:6379> bf.exists bf1 itemmadd1 (integer) 1 # 批量檢查多個元素是否存在於BloomFilter 127.0.0.1:6379> bf.mexists bf1 itemadd1 itemmadd1 itemmadd2 1) (integer) 1 2) (integer) 1 3) (integer) 1 ```c ### 2.2.7、BF.INSERT - 參數 - BF.INSERT {key} [CAPACITY {cap}] [ERROR {error}] [EXPANSION expansion] [NOCREATE] [NONSCALING] ITEMS {item...} - 功能 - 向key指定的Bloom中添加多個元素,添加時可以指定大小和錯誤率,且可以控制在Bloom不存在的時候是否自動創建 - 時間復雜度 - O(log N),N是過濾器的層數。 - 參數說明 - key:布隆過濾器的名字; - CAPACITY:[如果過濾器已創建,則此參數將被忽略]。更多的信息參考<bf.reserve>; - ERROR:[如果過濾器已創建,則此參數將被忽略]。更多的信息參考<bf.reserve>; - expansion:布隆過濾器會自動創建一個子過濾器,子過濾器的大小是上一個過濾器大小乘以expansion。expansion的默認值是2,也就是說布隆過濾器擴容默認是2倍擴容。 - NOCREATE:如果設置了該參數,當布隆過濾器不存在時則不會被創建。用於嚴格區分過濾器的創建和元素插入場景。該參數不能與CAPACITY和ERROR同時設置。 - NONSCALING:設置此項后,當添加到布隆過濾器中的數據達到初始容量后,不會擴容過濾器,並且會拋出異常((error) ERR non scaling filter is full)。 - ITEMS:待插入過濾器的元素列表,該參數必傳。 - 返回值 - 成功:返回一個數組,數組的每一個元素可能為1或0,當item一定不存在時數組元素值為1,當item可能已經存在時數組元素值為0。 - 其它情況返回相應的異常信息。 ```c 127.0.0.1:6379> del bfinsert (integer) 1 127.0.0.1:6379> bf.insert bfinsert CAPACITY 5 ERROR 0.1 EXPANSION 2 NONSCALING ITEMS item1 item2 1)