水煮Redisson(六)-布隆过滤器实现

875 阅读2分钟

前言

上一篇简单介绍了布隆过滤器,还用java做了一个简单实现,这里我们介绍Redisson和Guava的实现。
本文设定的误判率逻辑:在布隆过滤器中写入test0-test100000的数据,然后校验test100000-test200000是否在布隆过滤器中,如果存在,则表示发生了误判。

Redisson实现

Redisson中布隆过滤器的实现类是RBloomFilter,不能在集群模式中使用,从源码中可以看出其依赖于RedissonBitSet来实现,具体可以参考redis中的getBit指令
RBloomFilter中包含了几个对应的方法:

//初始化布隆过滤器,var1表示大小,var3表示容错率
boolean tryInit(long var1, double var3);  
//添加对象
boolean add(T var1);                      
//判断对象是否存在
boolean contains(T var1);                 
//返回预计插入数量
long getExpectedInsertions();             
//返回容错率
double getFalseProbability();             
//返回hash函数个数
int getHashIterations();                  
//对象插入后,估计插入数量
long count();                             
//布隆过滤器位数组的大小
long getSize();                           

根据设定的误判率,会自动改变集合的容量,比如代码中我们设定的误判率是0.03,集合的容量是729844;但是如果我设定误判率是0.9,那么size就会下调到21929。还有,设定的误判率和实际的误判率会有偏差,比如测试下来实际的误判率是0.07。
测试类

@Test
public void testRedissonBloom() {
    int size = 100000;
    RBloomFilter<Object> bloom = redissonClient.getBloomFilter("bloomKey");
    bloom.tryInit(size, 0.03);
    for (int i = 0; i < size; i++) {
        bloom.add("test" + i);
    }
    float mistakeCount = 0.0f;
    for (int i = size; i < size * 2; i++) {
        if (bloom.contains("test" + i)) {
            mistakeCount++;
        }
    }
    log.info("test5是否存在:{}", bloom.contains("test5"));
    log.info("abc是否存在:{}", bloom.contains("abc"));
    log.info("误判:{},误判率:{}", mistakeCount, mistakeCount / size);
    log.info("hash函数个数:{}", bloom.getHashIterations());
    log.info("容错率:{}", bloom.getFalseProbability());
    log.info("预计插入数量:{}", bloom.getExpectedInsertions());
    log.info("插入对象数量:{}", bloom.count());
    log.info("布隆过滤器位数组的大小:{}", bloom.getSize());
}

测试输出:

test5是否存在:true
abc是否存在:true
误判:7077.0,误判率:0.07077
hash函数个数:5
容错率:0.03
预计插入数量:100000
插入对象数量:98735
布隆过滤器位数组的大小:729844

Guava实现

Guava中的布隆过滤器还是一个beta版本
从实际的误判率来看,Guava的版本比Redisson要靠谱很多。设定误判率0.01,实际误判率0.0102。

public static void main(String[] args) {
    int size = 100000;
    double fpp = 0.01d;
    BloomFilter<String> bloomFilter = BloomFilter.create(
            Funnels.stringFunnel(Charset.defaultCharset()), size, fpp);
    for (int i = 0; i < size; i++) {
        bloomFilter.put("test" + i);
    }
    int mistakeCount = 0;
    for (int i = size; i < size * 2; i++) {
        if (bloomFilter.mightContain("test" + i)) {
            mistakeCount++;
        }
    }
    long approximateElementCount = bloomFilter.approximateElementCount();
    System.out.println("元素数量[大约]:"+approximateElementCount);
    double expectedFpp = bloomFilter.expectedFpp();
    System.out.println("设定的误判率:"+expectedFpp);
    System.out.println("误判数量:" + mistakeCount);
    System.out.println("误判率:" + (mistakeCount* 1.0 / size ));
}

测试输出

元素数量[大约]100013
设定的误判率:0.010044335605248233
误判数量:1020
误判率:0.0102