資料內容:
布隆過濾器就是一個大型的位數(shù)組和幾個不一樣的無偏 hash 函數(shù)。所謂無偏就是能夠把元素的 hash 值算得
比較均勻。
向布隆過濾器中添加 key 時,會使用多個 hash 函數(shù)對 key 進行 hash 算得一個整數(shù)索引值然后對位數(shù)組長度
進行取模運算得到一個位置,每個 hash 函數(shù)都會算得一個不同的位置。再把位數(shù)組的這幾個位置都置為 1 就
完成了 add 操作。
向布隆過濾器詢問 key 是否存在時,跟 add 一樣,也會把 hash 的幾個位置都算出來,看看位數(shù)組中這幾個位
置是否都為 1,只要有一個位為 0,那么說明布隆過濾器中這個key 不存在。如果都是 1,這并不能說明這個
key 就一定存在,只是極有可能存在,因為這些位被置為 1 可能是因為其它的 key 存在所致。如果這個位數(shù)組
比較稀疏,這個概率就會很大,如果這個位數(shù)組比較擁擠,這個概率就會降低。
這種方法適用于數(shù)據(jù)命中不高、 數(shù)據(jù)相對固定、 實時性低(通常是數(shù)據(jù)集較大) 的應用場景, 代碼維護較為
復雜, 但是緩存空間占用很少。
可以用redisson實現(xiàn)布隆過濾器,引入依賴:
1 <dependency>
2 <groupId>org.redisson</groupId>
3 <artifactId>redisson</artifactId>
4 <version>3.6.5</version>
5 </dependency>
示例偽代碼:
1 package com.redisson;
2
3 import org.redisson.Redisson;4 import org.redisson.api.RBloomFilter;
5 import org.redisson.api.RedissonClient;
6 import org.redisson.config.Config;
7
8 public class RedissonBloomFilter {
9
10 public static void main(String[] args) {
11 Config config = new Config();
12 config.useSingleServer().setAddress("redis://localhost:6379");
13 //構造Redisson
14 RedissonClient redisson = Redisson.create(config);
15
16 RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
17 //初始化布隆過濾器:預計元素為100000000L,誤差率為3%,根據(jù)這兩個參數(shù)會計算出底層的bit數(shù)組大小
18 bloomFilter.tryInit(100000000L,0.03);
19 //將zhuge插入到布隆過濾器中
20 bloomFilter.add("zhuge");
21
22 //判斷下面號碼是否在布隆過濾器中
23 System.out.println(bloomFilter.contains("guojia"));//false
24 System.out.println(bloomFilter.contains("baiqi"));//false
25 System.out.println(bloomFilter.contains("zhuge"));//true
26 }
27 }
使用布隆過濾器需要把所有數(shù)據(jù)提前放入布隆過濾器,并且在增加數(shù)據(jù)時也要往布隆過濾器里放,布隆過濾器
緩存過濾偽代碼:
1 //初始化布隆過濾器
2 RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
3 //初始化布隆過濾器:預計元素為100000000L,誤差率為3%
4 bloomFilter.tryInit(100000000L,0.03);
5
6 //把所有數(shù)據(jù)存入布隆過濾器
7 void init(){
8 for (String key: keys) {
9 bloomFilter.put(key);
10 }
11 }
12
13 String get(String key) {
14 // 從布隆過濾器這一級緩存判斷下key是否存在
15 Boolean exist = bloomFilter.contains(key);
16 if(!exist){
17 return "";
18 }
19 // 從緩存中獲取數(shù)據(jù)
20 String cacheValue = cache.get(key);
21 // 緩存為空22 if (StringUtils.isBlank(cacheValue)) {
23 // 從存儲中獲取
24 String storageValue = storage.get(key);
25 cache.set(key, storageValue);
26 // 如果存儲數(shù)據(jù)為空, 需要設置一個過期時間(300秒)
27 if (storageValue == null) {
28 cache.expire(key, 60 * 5);
29 }
30 return storageValue;
31 } else {
32 // 緩存非空
33 return cacheValue;
34 }
35 }