一、什麼是布隆過濾器?
布隆過濾器可以用來判斷一個元素是否在一個集合中。它的優勢是只需要佔用很小的內存空間以及有着高效的查詢效率
對於布隆過濾器而言,它的本質是一個位數組:位數組就是數組的每個元素都只佔用1bit ,並且每個元素只能是0或者1
布隆過濾器除了一個位數組,還有K個哈希函數。當一個元素加入布隆過濾器中的時候,會進行如下操作:
使用K個哈希函數對元素值進行K次計算,得到K個哈希值
根據得到的哈希值,在位數組中把對應下標的值置爲1
下圖表示有三個hash函數,比如一個集合中有x、y、z三個元素,分別用三個hash函數映射到二進制序列的某些位上,假設我們判斷w是否在集合中,同樣用三個hash函數來映射,結果發現取得的結果不全爲1,則表示w不在集合裏面
數組的容量即使再大,也是有限的。那麼隨着元素的增加,插入的元素就會越多,位數組中被置爲1的位置因此也越多,這就會造成一種情況:當一個不在布隆過濾器中的元素,經過同樣規則的哈希計算之後,得到的值在位數組中查詢,有可能這些位置因爲之前其它元素的操作先被置爲1了
所以,有可能一個不存在布隆過濾器中的會被誤判成在布隆過濾器中。這就是布隆過濾器的一個缺陷。但是,如果布隆過濾器判斷某個元素不在布隆過濾器中,那麼這個值就一定不在布隆過濾器中。總結就是:
- 布隆過濾器說某個元素在,可能會被誤判
- 布隆過濾器說某個元素不在,那麼一定不在
二、Google布隆過濾器基本使用
1)、引入依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
2)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);
}
}
3)、BloomFilterController
@RestController
public class BloomFilterController {
@Autowired
private BloomFilterService bloomFilterService;
@RequestMapping("/bloom/idExists")
public boolean ifExists(int id) {
return bloomFilterService.userIdExists(id);
}
}
三、Google布隆過濾器與Redis布隆過濾器對比
1)、Google布隆過濾器的缺點
- 基於JVM內存的一種布隆過濾器
- 重啓即失效
- 本地內存無法用在分佈式場景
- 不支持大數據量存儲
2)、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
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> bf.mexists urls www.baidu.com www.tianmao.com
1) (integer) 1
2) (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%的情況下),由於布隆過濾器有可能將一個不存在布隆過濾器中的誤判成在布隆過濾器中,也就是有可能會把非會員判斷爲會員,所以第二步查詢數據庫中用戶對應的數據庫信息判斷該用戶是否是會員。