布隆过滤器解决缓存穿透

redis缓存击穿透题解决

1、为什么用redis作为缓存

​ 使用缓存我们主要是保证系统的高性能高并发(单机mysql 能承受的最大2000qps 超过容易报警,而使用redis作为缓存,它是基于内存操作的天然支持高并发,而mysql是基于磁盘操作的,单机redis支持三万到十万的qps单机承载量很高)

2、使用redis作为缓存会导致哪些问题

​ 缓存穿透:

概念:用户需要查询一个数据,缓存中没有,也就是没有命中,于是向数据库中发起请求,发现也没有。当用户很多的时候,缓存都没有命中,于是都去请求数据库,这给数据库造成很大的压力。

解决方案

  • 布隆过滤器:是一种数据结构,对所有可能查询的参数以 hash 方式存储,先在控制层进行校验,不符合则丢弃,避免了过多访问数据库。
  • 缓存空对象:当存储层没有命中时,即使返回空对象也将其缓存起来。(意味着更多的空间存储,即使设置了过期时间,缓存和数据库还是有段时间数据不一致。)

缓存击穿:

概念:当一个 key 非常热点时,在不断扛高并发,集中对这个热点数据进行访问,当这个 key 失效的瞬间,请求直接到达数据库,给数据库瞬间的高压力。

解决方案

  • 设置热点数据永不过期
  • 加分布式锁:保证每个 key 同时只有一个线程去查询后端服务。

缓存雪崩:

概念:某个时间段,缓存集中失效

解决方案

  • 增加 Redis 集群的数量
  • 缓存过期时间的时候,错峰设置
  • 限流降级:在缓存失效后,通过加锁和队列来控制数据库写缓存的线程数量
  • 数据预热:正式部署之前,将数据预先访问一遍,让缓存失效的时间尽量均匀
3、布隆过滤器

缓存空对象存在的问题:如果遇到黑客利用不存在的key进行攻击,我们还是缓存空值,那么缓存将会失去意义

将所有可能的查询key 先映射到布隆过滤器中,查询时先判断key是否存在布隆过滤器中,存在才继续向下执行,如果不存在,则直接返回。布隆过滤器将值进行多次哈希bit存储,布隆过滤器说某个元素在,可能会被误判。布隆过滤器说某个元素不在,那么一定不在。

布隆过滤器的原理:当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点(offset),把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。

简单来说就是准备一个长度为 m 的位数组并初始化所有元素为 0,用 k 个散列函数对元素进行 k 次散列运算跟 len(m)取余得到 k 个位置并将 m 中对应位置设置为 1。
img

优缺点: 布隆过滤器 空间占用极小插入和查询速度是非常快 ,但存在误差(哈希冲突导致误判)

4、实现

1、将数据告知布隆过滤器

​ 应用启动时初始化布隆过滤器

RBloomFilter<SomeObject> bloomFilter = redisson.getBloomFilter("sample");
// 初始化布隆过滤器,预计统计元素数量为55000000,期望误差率为0.03
bloomFilter.tryInit(55000000L, 0.03);
bloomFilter.add(new SomeObject("field1Value", "field2Value"));
bloomFilter.add(new SomeObject("field5Value", "field8Value"));
bloomFilter.contains(new SomeObject("field1Value", "field8Value"));

springboot提供两个接口:IOC容器创建完成后,初始化一些内容 用CommandlineRunner或者 ApplicationRuner

 @Override
    public void run(String... args) throws Exception {
        RBloomFilter<Object> rbloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
        // 初始化布隆过滤器,预计统计元素数量为100000,期望误差率为0.01
        rbloomFilter.tryInit(100000, 0.01);
    }

添加到布隆过滤器

 //添加布隆过滤
    RBloomFilter<Long> rbloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
    rbloomFilter.add(skuInfo.getId());

2、布隆过滤器判断是否存在(过滤)

用户发来请求时,使用布隆过滤器来判断是否包含

//添加布隆过滤
    RBloomFilter<Long> rbloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
    //不包含
    if(!rbloomFilter.contains(skuId)) return map;

如果包含就去读redis,如果redis没有就去读数据库

参考文章:https://blog.csdn.net/jfwan/article/details/121810096

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值