【Redis】缓存穿透问题的解决思路

缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,既然都不存在,那它永远都不可能建立缓存,因此只要有人去请求,它一定会到达数据库。

例如根据id查询店铺,查询缓存和数据库中数据都不存在,也就是说用户传过来的就是一个根本不存在的id。当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,如果是一个不怀好意的人,它整了无数的线程,并发的向这个不存在的数据发起请求,这样所有的请求都会到达数据库,很有可能就将我们的数据库搞垮,这就是缓存穿透带来的危害了。

image-20240526101530901

常见的解决方案有两种:

  • 缓存空对象。这是一种简单暴力的处理方法。

    思路:你来请求我,redis中没有,然后去数据库中查也没有,为了让你不再请求我了,我就把这个空的值缓存到redis中。也就是说你请求的时候传了个id,这个id是根本没有的,我也给你缓存起来,这样下次你再来的时候,redis就命中了,尽管命中是个null,至少也是命中了,这样就不会请求我们的数据库了,是不是简单粗暴的一个思路。

    image-20240526110035859
    • 优点:实现简单,维护方便

    • 缺点:

      • 额外的内存消耗。不存在的id都缓存起来了,这样会导致redis中缓存了很多很多垃圾。不过针对这个问题其实也能解决:缓存null的时候加一个比较短的ttl,五分钟甚至两分钟,在这两分钟内缓存是有效的,当有恶意的用户来请求的时候,也会起到一定的保护作用,同时它的有效期也不长,因此这些垃圾数据过段时间就会清除。

      • 可能造成短期的不一致。

        例如用户请求一个id,这个id刚好不存在,我们给它设置为null,但就在此时,我们真的给这个id插入了一条店铺的数据,这个时候等于数据库中已经有了,但我们却缓存了一个null,此时用户来查查到的是null,而实际上是存在的,此时就出现不一致了,只有当ttl过期后,用户才能查到最新的这样一个数据。

        针对这个问题其实我们只需要去控制ttl的时间,在一定程度上其实是可以缓解的,不一致的时期只要足够短,其实也是可以接受的。

        当然如果你实在无法接受这个不一致,你也可以这么做:当我们新增一条数据的时候,我们主动将这条数据插入缓存中,覆盖之前的null。

  • 布隆过滤

    布隆过滤准确来讲是一种算法,它的做法是在客户端与redis之间又加入了一层这样的拦截器,叫做 布隆过滤器,当用户请求来了后,不是上来就查redis,而是先去找布隆过滤,问一问这个数是存在还是不存在,如果这个数据不存在,会直接拒绝,就不给你机会往下走了。如果布隆过滤器告诉你存在,此时它才会去redis中查询,去redis查询后,剩下的逻辑就跟以前一样了。

    如果redis中有,直接返回;如果redis没有,就查询数据库,数据库有就缓存到redis,并且将数据返回。

    image-20240526110540787

    那布隆过滤器它怎么知道我这个数据有还是没有呢?难道布隆过滤器里存储了数据库中所有的数据吗?并不是的,如果是这样,我们就多余存储了很多数据了。布隆过滤器的原理,你可以简单理解为是一个byte数组,里面存的就是二进制位。当我们去判断数据库中的数据是否存在的时候,并不是真的把数据存储到布隆过滤器,而是将这些数据基于某一种哈希算法,计算出哈希值,然后再将这些哈希值转化为二进制位,保存到布隆过滤器中。然后我们去判断数据是否存在的时候,其实就是判断对应的位置是0还是1,以此来去判断数据是否存在,这种存在与否是一种概率上的统计,它并不是真的百分之百的准确,因此当你问布隆过滤器是否存在,它说不存在的时候,那就是真的不存在,但是当它告诉你存在的时候,其实不一定就是存在的,因为只要有哈希思想,就可能存在哈希冲突。

    因此如果我现在来请求布隆过滤器,它告诉我说这个数据存在,放行了,然后我去redis查询没发现,然后去数据库查询,又没发现,就又一次出现穿透了,因此还是有一定穿透风险的。

    • 优点:内存占用较少,不用像之前那样缓存大量空数据,它只是用一个二进制位的形式保存数据,所以它的空间占用非常小。
    • 缺点:
      • 实现复杂。如果你想自己实现布隆过滤器,其实还是挺麻烦的,好在redis中其实提供了Bitmap (位图),是它自带的一种不能过滤的实现,可以帮助我们简化开发。
      • 存在误判可能

1653326156516

因此在我们开发过程中会选择方案一:缓存空对象。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值