【redis】缓存穿透的解决方案

解决缓存穿透 我们得首先知道什么是缓存穿透吧?

  • 缓存穿透就是在缓存中没有查到数据,而去数据库去查询。少量的缓存穿透对系统是没有影响的。但是大量的访问数据库则是有问题的,如2/8原则,百分之20是重要的数据 ,百分之80是次要的。也就是百分之20可以去访问DB而百分之80去访问cache。

1.回种空值

  • 当有大量的无效用户访问缓存时这个时候肯定不会有key 则就会大量请求数据库,然后再去数据库去查也不存在这个值,那就将这个值以value为空值的形式回填到缓存,且设置过期时间。
  • 缺点:短时间内有大量的不存在值请求的时候,那这时很快的挤爆缓存,还会剔除掉一些已经被缓存的用户信息反而会造成缓存命中率的下降。这样的方案我们得看一下缓存的容量来判断一下该方案是否可取。

2. 使用布隆过滤器

  • 什么是布隆过滤器?我们把集合中的每一个值按照提供的 Hash 算法算出对应的 Hash 值,然后将 Hash 值对数组长度取模后得到需要计入数组的索引值,并且将数组这个位置的值从 0 改成 1。在判断一个元素是否存在于这个集合中时,你只需要将这个元素按照相同的算法计算出索引值,如果这个位置的值为 1 就认为这个元素在集合中,否则则认为不在集合中。
  • 如何使用布隆过滤器来解决缓存穿透的问题呢? 将数据库的ID计算hash值并映射到大数组中,且将映射到的位置的值进行存1,其他值设为0,当新的数据存储到DB的时候不仅要更新表还要更新这个布隆过滤器的大数组里面。当有新用户进行请求的时候以同样的hash算法确定到大数组的某一个位置去对比他的值是不是1,如果是1去缓存拿数据,不是直接返回空。这就将缓存和数据库都保护了起来。如下图
    在这里插入图片描述
  • 缺点:
  1. 在判断元素是否存在的时候有可能计算错误,就针对于hash算法来说吧,就有可能出现hash碰撞。导致不同的值出现相同的结果。

解决方案:使用多个 Hash 算法为元素计算出多个 Hash 值,只有所有 Hash 值对应的数组中的值都为 1 时,才会认为这个元素在集合中。
2. 不支持删除元素,布隆过滤器不支持删除元素的缺陷也和 Hash 碰撞有关。给你举一个例子,假如两个元素 A 和 B 都是集合中的元素,它们有相同的 Hash 值,它们就会映射到数组的同一个位置。这时我们删除了 A,数组中对应位置的值也从 1 变成 0,那么在判断 B 的时候发现值是 0,也会判断 B 是不在集合中的元素,就会得到错误的结论

解决方案:我会让数组中不再只有 0 和 1 两个值,而是存储一个计数。比如如果 A 和 B 同时命中了一个数组的索引,那么这个位置的值就是 2,如果 A 被删除了就把这个值从 2 改为 1。这个方案中的数组不再存储 bit 位,而是存储数值,也就会增加空间的消耗。所以,你要依据业务场景来选择是否能够使用布隆过滤器,比如像是注册用户的场景下,因为用户删除的情况基本不存在,所以还是可以使用布隆过滤器来解决缓存穿透的问题的。

3. 使用分布式锁

  • 这种方式解决的问题是有一个及热点数据,一旦缓存失效就会有大量的数据涌进数据库,导致数据库垮掉。这种也叫狗桩效应。
  • 当这个热点缓存失效后立马其中一个线程,穿透到数据库,将数据家宅到缓存中,在缓存未加载之前,所有这个缓存的请求不再穿透全部返回。
  • 通过在 Memcached 或者 Redis 中设置分布式锁,只有获取到锁的请求才能够穿透到数据库。
    分布式锁的方式也比较简单,比方说 ID 为 1 的用户是一个热点用户,当他的用户信息缓存失效后,我们需要从数据库中重新加载数据时,先向 Memcached 中写入一个 Key 为"lock.1"的缓存项,然后去数据库里面加载数据,当数据加载完成后再把这个 Key 删掉。这时,如果另外一个线程也要请求这个用户的数据,它发现缓存中有 Key 为“lock.1”的缓存,就认为目前已经有线程在加载数据库中的值到缓存中了,它就可以重新去缓存中,不再穿透数据库了。
    参考文章: https://time.geekbang.org/column/article/153104(极客时间)
原创文章 92 获赞 11 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_40413961/article/details/104784003