布隆过滤器原理

文章介绍了布隆过滤器的概念、特点和工作原理,特别是其在解决Redis缓存穿透问题中的应用。通过使用Redisson客户端创建布隆过滤器,并结合Redis存储,演示了如何添加数据、查询数据以及处理可能存在误判的情况。同时,提到了缓存击穿的解决方案,如设置互斥锁、使用差异失效时间和搭建Redis集群等。
摘要由CSDN通过智能技术生成

是什么

由一个初值都为零的bit数组和多个哈希函数构成,来判断某个数据是否存在。

特点:

  • 占用空间少,高效插入和查询
  • 存在误判的情况,因为不同的Key可能计算出相同的hash值
  • 一个元素如果判断结果为不存在则一定不存在!

原理

  1. 初始化
  2. 添加:当我们向布隆过滤器中添加数据时,为了尽量地址不冲突,会使用多个 hash 函数对 key 进行运算,算得一个下标索引值,然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
  3. 判断是否存在:向布隆过滤器查询某个key是否存在时,先把这个 key 通过相同的多个 hash 函数进行运算,查看对应的位置是否都为 1,
    只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在;
    如果这几个位置全都是 1,那么说明极有可能存在;

使用场景

在这里插入图片描述

  • 解决redis缓存穿透问题
    使用Docker安装 带有RedisBloom的镜像文件
public class RedissonBloomFilterDemo
{
    public static final int _1W = 10000;

    //布隆过滤器里预计要插入多少数据
    public static int size = 100 * _1W;
    //误判率,它越小误判的个数也就越少
    public static double fpp = 0.03;

    static RedissonClient redissonClient = null;
    static RBloomFilter rBloomFilter = null;

    static
    {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);
        //构造redisson
        redissonClient = Redisson.create(config);
        //通过redisson构造rBloomFilter
        rBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec());

        rBloomFilter.tryInit(size,fpp);

        // 1测试  布隆过滤器有+redis有
        rBloomFilter.add("10086");
        redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086");

        // 2测试  布隆过滤器有+redis无
        //rBloomFilter.add("10087");

        //3 测试 ,都没有

    }

    public static void main(String[] args)
    {
        String phoneListById = getPhoneListById("10087");
        System.out.println("------查询出来的结果: "+phoneListById);

        //暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        redissonClient.shutdown();
    }

    private static String getPhoneListById(String IDNumber)
    {
        String result = null;

        if (IDNumber == null) {
            return null;
        }
        //1 先去布隆过滤器里面查询
        if (rBloomFilter.contains(IDNumber)) {
            //2 布隆过滤器里有,再去redis里面查询
            // getBucket获取可以操作redis字符串的对象
            RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec());
            result = rBucket.get();
            if(result != null)
            {
                return "i come from redis: "+result;
            }else{
                result = getPhoneListByMySQL(IDNumber);
                if (result == null) {
                    return null;
                }
                // 重新将数据更新回redis
                redissonClient.getBucket(IDNumber,new StringCodec()).set(result);
            }
            return "i come from mysql: "+result;
        }
        return result;
    }

    private static String getPhoneListByMySQL(String IDNumber)
    {
        return "chinamobile"+IDNumber;
    }

}

补充

  • 缓存击穿:热点Key过期
    1. 热点Key不设置过期时间
    2. 互斥锁防止缓存击穿
    3. 随即退避:让线程随机短暂等待,再去检查缓存中是否有数据,没有的话再去数据库查
    4. 差异失效时间:设置两个缓存a、b,设置不同的过期时间。查询先a后b,更新先b后a,此顺序可以保证即使a缓存没有数据也能从b缓存中获取数据
  • 缓存雪崩:大量的Key过期、主机挂掉了
    1. 搭建Redis集群
    2. 使用本地缓存(Caffine)+ 限流框架sentinel等
    3. 开启Redis持久化机制 (AOF + RDB)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值