布隆过滤器
是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。但是布隆过滤器可以控制错误率。
具体的布隆过滤器相关的内容可查找相关资料,非常详细,其优势就是占用内存比hash表要小得多,非常适合用于做过滤的场景
Guava中的布隆过滤器
Guava是google开发的java基础库,其中提供了布隆过滤器的实现,即名为BloomFilter的类,其使用方式类似如下:
![9eea8235bcc00ab50d722dd12d7bb938.png](https://img-blog.csdnimg.cn/img_convert/9eea8235bcc00ab50d722dd12d7bb938.png)
使用Redis实现布隆过滤器
当布隆过滤器也需要使用大量内存,并要求在多台机器之间共享时,Guava提供的BloomFilter就难以满足需求了。BloomFilter在数据存在上,实际上可以认为是一个非常大了位图,而redis支持bitmap数据结构,正好可以用于实现布隆过滤器。
然而,我们如何实现BloomFilter呢,我们可以先看看guava中的BloomFilter的实现方式:
![ab634a9d8523be1be830d63d433b68bb.png](https://img-blog.csdnimg.cn/img_convert/ab634a9d8523be1be830d63d433b68bb.png)
BloomFilter.put()方法中,直接调用了strategy.put(),我们可以继续进入到这个Strategy中:
![e9fcddd4a4ed9411c6b6a578ecdb01f2.png](https://img-blog.csdnimg.cn/img_convert/e9fcddd4a4ed9411c6b6a578ecdb01f2.png)
可以看到,Strategy是BloomFilter类中的内部接口,是用于当布隆过滤器存储的对象转换成bits,guava中提供的实现是一个enum:
![03f20499f8da4c8c22d9e47f4bb8d7b6.png](https://img-blog.csdnimg.cn/img_convert/03f20499f8da4c8c22d9e47f4bb8d7b6.png)
我们继续看看其put方法的实现:
![047cb370b373414b047ce19154def6ca.png](https://img-blog.csdnimg.cn/img_convert/047cb370b373414b047ce19154def6ca.png)
其中,除了hash以外,就是对LockFreeBitArray的操作,因此,如果我们能通过redis实现一个新的LockFreeBitArray,那我们就能实现一个基于redis的布隆过滤器了,但是很可惜,LockFreeBitArray是final的类,且是包访问权限,我们无法从LockFreeBitArray类做扩展。
那么我们只有使用两种方式:
1. 自己从头开始实现BloomFilter
2. 拿来主义,都是开源的了,抄代码吧,把BloomFilter相关的代码copy出来,替换掉LockFreeBitArray
我这里使得了第二种方式,将guava中的BloomFilter复制一份,并加上JedisPool参数用于访问redis,然后基于redis实现一个LockFreeBitArray,其中基于redis的LockFreeBitArray的实现如下:
![9c8928234192b3b41fe2c0409a2a646c.png](https://img-blog.csdnimg.cn/img_convert/9c8928234192b3b41fe2c0409a2a646c.png)
![661a87fccc09670485ace388f8f7e0a5.png](https://img-blog.csdnimg.cn/img_convert/661a87fccc09670485ace388f8f7e0a5.png)
![615386c8be7aab6339f45bf8c6da4a18.png](https://img-blog.csdnimg.cn/img_convert/615386c8be7aab6339f45bf8c6da4a18.png)
![9bf4b089b671b430818121988fbd816f.png](https://img-blog.csdnimg.cn/img_convert/9bf4b089b671b430818121988fbd816f.png)
可以看到,本质上就是通过一个key创建出一个bitmap,代码本身只是将原来guava的LockFreeBitArray中的byte数据替换成了redis和bitmap
整个BloomFilterStrategies的重新实现如下:
最后是与Guava的BloomFilter几乎一样的RedisBloomFilter
![02abd9df106f6d3b776347346c523bc5.png](https://img-blog.csdnimg.cn/img_convert/02abd9df106f6d3b776347346c523bc5.png)
待优化点
目前的环境中使得的redis是单机的,所以这样使用是没问题的,但是对于使用redis集群而言,这样做就不太好了,因为整个BloomFilter只关联了一个key,无法分散到redis集群中的各台机器上,因此可以针对集群做一个优化,一种可行的思路就是将一个BloomFilter拆分成多个BloomFilter,生成不同的key,将BloomFilter的数据分散到redis集群中不同的redis机器上,这样可充分发挥出redis集群的性能和缓存的容量