一、什么是布隆過濾器?
布隆過濾器可以用來判斷一個元素是否在一個集合中。它的優勢是只需要占用很小的內存空間以及有着高效的查詢效率。
對於布隆過濾器而言,它的本質是一個位數組:位數組就是數組的每個元素都只占用1bit ,並且每個元素只能是0或者1
布隆過濾器除了一個位數組,還有 K 個哈希函數。當一個元素加入布隆過濾器中的時候,會進行如下操作:
- 使用K個哈希函數對元素值進行K次計算,得到K個哈希值
- 根據得到的哈希值,在位數組中把對應下標的值置為1
下圖表示有三個hash函數,比如一個集合中有x、y、z三個元素,分別用三個hash函數映射到二進制序列的某些位上,假設我們判斷w是否在集合中,同樣用三個hash函數來映射,結果發現取得的結果不全為1,則表示w不在集合里面。
數組的容量即使再大,也是有限的。那么隨着元素的增加,插入的元素就會越多,位數組中被置為1的位置因此也越多,這就會造成一種情況:當一個不在布隆過濾器中的元素,經過同樣規則的哈希計算之后,得到的值在位數組中查詢,有可能這些位置因為之前其它元素的操作先被置為1了
所以,有可能一個不存在布隆過濾器中的會被誤判成在布隆過濾器中。這就是布隆過濾器的一個缺陷。但是,如果布隆過濾器判斷某個元素不在布隆過濾器中,那么這個值就一定不在布隆過濾器中。總結就是:
- 布隆過濾器說某個元素在,可能會被誤判
- 布隆過濾器說某個元素不在,那么一定不在
二、Google布隆過濾器基本使用
- 引入依賴
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency>
- BloomFilterService
@Service
public class BloomFilterService {@Autowired private UserMapper userMapper; private BloomFilter<Integer> bf; /** * 創建布隆過濾器 * * @PostConstruct:程序啟動時候加載此方法 */ @PostConstruct public void initBloomFilter() { List<User> userList = userMapper.selectAllUser(); if (CollectionUtils.isEmpty(userList)) { return; } //創建布隆過濾器(默認3%誤差) bf = BloomFilter.create(Funnels.integerFunnel(), userList.size()); for (User user : userList) { bf.put(user.getId()); } } /** * 判斷id可能存在於布隆過濾器里面 * * @param id * @return */ public boolean userIdExists(int id) { return bf.mightContain(id); }
}
- BloomFilterController
@RestController
public class BloomFilterController {@Autowired private BloomFilterService bloomFilterService; @RequestMapping("/bloom/idExists") public boolean ifExists(int id) { return bloomFilterService.userIdExists(id); }
}
三、Google布隆過濾器與Redis布隆過濾器對比
- Google布隆過濾器的缺點
基於JVM內存的一種布隆過濾器
重啟即失效
本地內存無法用在分布式場景
不支持大數據量存儲
- Redis布隆過濾器
可擴展性Bloom過濾器:一旦Bloom過濾器達到容量,就會在其上創建一個新的過濾器
不存在重啟即失效或者定時任務維護的成本:基於Google實現的布隆過濾器需要啟動之后初始化布隆過濾器
缺點:需要網絡IO,性能比Google布隆過濾器低
四、Redis布隆過濾器安裝和基本使用
1)使用Docker安裝
[root@localhost ~]# docker run -d -p 6379:6379 --name bloomfilter redislabs/rebloom
2)基本使用
[root@localhost ~]# docker exec -it bloomfilter /bin/bash
root@7a06a3528556:/data# redis-cli -p 6379
127.0.0.1:6379>
Redis布隆過濾器命令:
bf.add:添加元素到布隆過濾器中,只能添加一個元素,如果想要添加多個使用bf.madd命令
bf.exists:判斷某個元素是否在過濾器中,只能判斷一個元素,如果想要判斷多個使用bf.mexists
127.0.0.1:6379> bf.add urls www.taobao.com
(integer) 1
127.0.0.1:6379> bf.exists urls www.taobao.com
(integer) 1
127.0.0.1:6379> bf.madd urls www.baidu.com www.tianmao.com
- (integer) 1
- (integer) 1
127.0.0.1:6379> bf.mexists urls www.baidu.com www.tianmao.com- (integer) 1
- (integer) 1
上面使用的布隆過濾器只是默認參數的布隆過濾器,它在我們第一次add的時候自動創建。Redis還提供了自定義參數的布隆過濾器,需要在add之前使用bf.reserve指令顯式創建。如果對應的key已經存在,bf.reserve會報錯(error) ERR item exists bf.reserve 過濾器名 error_rate initial_size
布隆過濾器存在誤判的情況,在Redis中有兩個值決定布隆過濾器的准確率:
- error_rate:允許布隆過濾器的錯誤率,這個值越低過濾器的位數組的大小越大,占用空間也就越大
- initial_size:布隆過濾器可以儲存的元素個數,當實際存儲的元素個數超過這個值之后,過濾器的准確率會下降
127.0.0.1:6379> bf.reserve user 0.01 100
OK
五、會員轉盤抽獎
實現思路:在判斷用戶是否是會員的時候,第一步先通過布隆過濾器過濾掉99%的非會員(誤碼率為1%的情況下),由於布隆過濾器有可能將一個不存在布隆過濾器中的誤判成在布隆過濾器中,也就是有可能會把非會員判斷為會員,所以第二步查詢數據庫中用戶對應的數據庫信息判斷該用戶是否是會員
聲明:本號所有文章除特殊注明,都為原創,公眾號讀者擁有優先閱讀權,未經作者本人允許不得轉載,否則追究侵權責任。
關注我的公眾號,后台回復【JAVAPDF】獲取200頁面試題!
5萬人關注的大數據成神之路,不來了解一下嗎?
5萬人關注的大數據成神之路,真的不來了解一下嗎?
5萬人關注的大數據成神之路,確定真的不來了解一下嗎?