布隆过滤器解决缓存穿透方案附上代码

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

概述

缓存击穿,大家多多少少听说过。那么这个问题到底是个什么问题?怎么解决呢?这个问题其实是在高并发场景下,使用缓存提高访问速度,一些非法访问或者说攻击刻意绕过缓存,来直接攻击我们数据库。导致我们缓存形同虚设,并没有起到缓存效果。这个问题解决的根本就是过滤掉非法请求,只允许访问合法数据即可,而布隆过滤器就是用来过滤非法请求的,能达到我们预想的效果。

布隆过滤器原理

原理图

image.png

1.首先会初始化一个全0比特位的数组,这个数组长度取决于用户期望存储数据的个数size,以及用户期望的误判率 fpp,根据用户size和fpp,会计算一个能存储下的bit数组,进行初始化,并且确定使用几个hash函数。

2.用户每存一条数据,同时将key设置到布隆过滤器中,那么布隆过滤器会根据初始化的多个hash函数分别进行计算 ,每个hash函数会计算一个布隆过滤器数组的下标,并且置为1

3.当用户判断一个key存在不存在时,布隆过滤器会重复2的步骤,看看是否全为1,如果是,那么数据存在

4.存在hash冲突,很有可能两个不同key通过hash计算,最后的数组下标会一模一样,从而导致误判。但是不存在的 数据一定不存在。

优缺点

优点:节省空间,不需要存储真实数据来判断数据是否存在

缺点:存在一定误判,因为hash冲突原因,是会产生误判的,虽然可以设置,但是性能会受一定影响

解决缓存穿透问题

布隆数据预热

/**
 * 数据预热Bloom
 */
public void readyBloom(){
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.select("id");
    List<User> users = userMapper.selectList(userQueryWrapper);
    for (User user:users){
        bloomFilter.put(user.getId());
    }
}
复制代码

1.一般情况下,布隆过滤器都是业务后期加入的,业务初期数据库压力不大,一些缓存穿透攻击可以接受,不会影响正常业务处理;一般都是后期业务压力变大,再加入一些非法攻击,会导致服务雪崩宕机之类风险
2.布隆过滤器预热,只需要将将来作为判断的key加入布隆过滤器即可

布隆过滤器同步增量更新

public String guavaBloomTestSet(User user){
    //插入数据
    userMapper.insert(user);
    //加到布隆过滤器
    bloomFilter.put(user.getId());

    return "success";
}
复制代码

更新数据同时,需要同步到布隆过滤器中,否则导致有数据查不出来的情况

扫描二维码关注公众号,回复: 13690081 查看本文章

布隆过滤器过滤非法数据处理

public User guavaBloomTestGet(@PathVariable Integer id){

    //判断布隆过滤器是否存在
    if(bloomFilter.mightContain(id)){

        //获取缓存数据
        String s = stringRedisTemplate.opsForValue().get(PREFIX+id.toString());

        if(s==null){
            //缓存不存在,查询数据库
            User user = userMapper.selectById(id);
            //空值处理(布隆误判 | 数据被删)
            String value=user==null?"":JSONUtil.toJsonStr(user);
            //加入缓存
            stringRedisTemplate.opsForValue().set(PREFIX+id.toString(), value, Duration.ofSeconds(300L));
            return user;
        }else if(s.equals("")){
            //缓存空值处理
            return null;
        }else {

            return JSONUtil.toBean(s,User.class);
        }

    }

    //不存在布隆过滤器直接返回
    return null;
}
复制代码

1.key存在布隆过滤器中,缓存没数据,那么查数据库(有数据),同时更新缓存

2.key存在布隆过滤器中,缓存没数据,那么查数据库(无数据),同时更新缓存(更新空值),属于误判的情况,或者数据之前存在,业务需求被删除

3.key存在布隆过滤器中,缓存有数据(空值),直接返回

4.key存在布隆过滤器中,缓存有数据(有值),直接返回(反序列化)

5.key不存在布隆过滤器中,直接返回null

扩展

基于场景考虑,有单机版布隆过滤器(guava),有分布式版布隆过滤器(redisson)

总结

通过布隆过滤器来拦截一些不在业务之内的非法请求,从而达到保护服务的作用;注意布隆并不能分担正常业务的流量压力。

猜你喜欢

转载自juejin.im/post/7068578263638999048