布隆过滤器原理浅析

前言

熟悉Redis缓存应用的伙伴们都知道,Redis缓存存在缓存击穿、雪崩和穿透的问题,通常在解决缓存穿透问题时,除了缓存异常请求外,还有一个叫做布隆过滤器的方案。下面,我们认识下布隆过滤器。

结构&原理

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。根据上述百度百科的描述,我们可以大致画出布隆过滤器的结构,如下
在这里插入图片描述
在初始情况下,我们可以将其看作一个很长的二进制数组,默认每个位置为0,当新增元素的时候,通过一系列Hash函数计算出这个元素的向量下标,然后填入二进制数组中,如下
在这里插入图片描述
这样,“张三”通过n个Hash函数映射到n个数组下标的位置上标记,同理,这时如果再来“李四”、“王五”等元素,也是通过n个Hash函数映射,这样,就有可能会导致有些值通过不通的Hash函数映射到同一个下标的位置,如下所示
在这里插入图片描述
在这里插入图片描述
那么,如何查询元素呢?在查询元素时,也是通过n个不同hash函数将元素映射到n个点,然后判断这n个位置的值是否为1,这样,就会有两个结论:

  • n个位置全是1,元素可能存在
  • 有至少一个位置是0,元素不存在

从上图我们可以看出,张三这个元素查询时,可能是由李四和王五两个元素的位置覆盖得出的1,因此,即使所有位置都为1,也不能完全确认张三存在。基于此问题,我们可以调整布隆过滤器的长度和随机映射函数,来减少元素碰撞。同样基于上述情况,我们可以很清晰的知道,布隆过滤器万万不能删除元素,因为你不知道同一下标对应多少元素。

SpringBoot简单测试

谷歌开源的Guava自带布隆过滤器,首先引入依赖

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

然后,我们设置好布隆过滤器的长度和误判率,插入对应的数据,例如插入1-1000000作为100万个测试数据,然后,查询1000001-2000000数据在布隆过滤器是否存在,查看它的误判数,从而计算真实的误判率,如下

package com.example.test.redis;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.text.NumberFormat;

public class BloomFilterTest {
    /**
     * 预计要插入多少数据
     */
    private static int size = 1000000;
    /**
     * 期望的误判率
     */
    private static double fpp = 0.001;
    /**
     * 布隆过滤器
     */
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(),size,fpp);

    public static void main(String[] args) {
        //插入样本数据
        for(int i=0; i<size; i++){
            bloomFilter.put(i);
        }

        //另外十万测试数据,预测误判率
        int count = 0;
        for(int i=size; i<size+1000000; i++){
            if(bloomFilter.mightContain(i)){
                count++;
                System.out.println(i+"误判了");
            }
        }
        NumberFormat nf = NumberFormat.getInstance();
        //关闭科学计数法
        nf.setGroupingUsed(false);
        //定义保留几位小数
        nf.setMaximumFractionDigits(8);
        System.out.println("总共误判数:"+count+",真实误判率为:"+nf.format((double)count/1000000.00));
    }
}

运行结果如下
在这里插入图片描述
如果我们调整判断数据量,会发现误判率超过了我们的期望值,这也说明随着数据量增大,误判率也会增加,这就要我们调整布隆过滤器的长度和随机映射函数了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雅俗共赏zyyyyyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值