目录
- 初识布隆过滤器
- 使用布隆过滤器
- 布隆过滤器如何实现
- 布隆过滤器使用场景
- 布隆过滤器存在问题
- 解决策略
初识布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
使用布隆过滤器
在Java中可以使用第三方库如Guava来实现布隆过滤器。
在guava包中的BloomFilter源码中,构造一个BloomFilter对象有四个参数:
- Funnel funnel:数据类型,由Funnels类指定即可
- long expectedInsertions:预期插入的值的数量
- fpp:错误率
- BloomFilter.Strategy:hash算法
使用Guava库实现布隆过滤器的示例代码如下:
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;public class BloomFilterExample {public static void main(String[] args) {// 创建布隆过滤器,设置预计插入的元素数量和误判率BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.unencodedCharsFunnel(), 1000, 0.01);// 添加元素到布隆过滤器bloomFilter.put("element1");bloomFilter.put("element2");// 判断元素是否存在于布隆过滤器中boolean exists = bloomFilter.mightContain("element1");System.out.println("Element exists: " + exists);}
}
布隆过滤器的预期误判率fpp是根据实际需求来设置的,一般情况下是根据对误判率的容忍度来确定的。误判率越低,布隆过滤器的空间和资源消耗也会增加。
布隆过滤器如何实现
BloomFilter 是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的,在初始状态时,相当于一个大型位数组,它的所有位都被置为0,
- 插入元素,经过n个hash函数散列映射到位图中的n个点,并置为1
- 查询元素,元素根据函数映射的n个点,检测n个点是否为1,有一个为0则证明元素不存在
所以可以判断元素一定不存在
判断某样东西一定不存在或者可能存在
原因在于,它通过多个hash函数进行散列,渐渐的数组上的1位越来越多,后续查询的元素映射的bit位也是1,一个bit位被多次映射为1,所以就不能判断这个元素是否存在
布隆过滤器的优点:
- 时间复杂度低,增加和查询元素的时间复杂为O(N),(N为哈希函数的个数,通常情况比较小)
- 保密性强,布隆过滤器不存储元素本身
- 存储空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间的(相比其他数据结构如Set集合)
布隆过滤器的缺点:
- 有一定的误判率,但是可以通过调整参数来降低
- 无法获取元素本身
- 很难删除元素
布隆过滤器使用场景
缓存穿透
应对缓存穿透问题,我们可以使用布隆过滤器。
一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。
大量数据判断是否存在
这个中间层,是不是用HashMap就好了呢?HashMap时间复杂度可以达到O(1),但是因为HashMap数据是在内存里面的,如果大量的数据远超出了服务器的内存就无法使用HashMap,可以使用布隆过滤器来做这个缓冲,因为它只存储映射的位点
布隆过滤器存在问题
随着插入到位图的key越来越来,哈希碰撞频率也会越来越高,导致它有一定的误判率,可以用来判断元素一定不存在,但是不能保证元素一定存在
解决策略
- 调整布隆过滤器的参数:布隆过滤器的参数包括哈希函数的个数和位数组的大小。增加哈希函数的个数和位数组的大小可以降低误判率,但会增加存储空间和查询时间。根据应用场景的需求,可以根据误判率的可接受程度进行调整。
- 结合其他数据结构:可以将布隆过滤器与其他数据结构结合使用,以进一步降低误判率。例如,可以使用一个较小的布隆过滤器进行快速的初始过滤,然后对可能存在的元素进行进一步验证,例如使用一个哈希表或数据库进行准确的查询。
- 使用多个独立的布隆过滤器:使用多个独立的布隆过滤器,每个布隆过滤器使用不同的参数和哈希函数。当需要判断元素是否存在时,需要通过多个布隆过滤器的判断结果进行综合。这样可以降低误判率,但也会增加存储空间和查询时间。
- 添加计数器:在布隆过滤器的基础上,可以为每个哈希函数维护一个计数器,记录元素的出现次数。当判断元素存在时,可以检查所有哈希函数的计数器值,如果存在任一计数器值为0,则判断为误判。
- 定期重建布隆过滤器:定期重建布隆过滤器可以清除已经过时的元素,并减少误判率。定期重建的频率可以根据应用场景的需要进行调整。