Redis的缓存穿透、缓存雪崩、缓存击穿问题及解决方案

发布时间 2023-09-10 22:45:56作者: 程序侠

Redis的缓存穿透、缓存雪崩、缓存击穿问题及解决方案

缓存穿透问题及解决思路

缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。因为我们查数据通常是现在redis缓存查数据,如果redis没有这个数据,就会去数据库查。如果张三想要搞垮数据库,就会发送大量请求来查询redis和数据库都没有的数据库,在这样高并发请求下,数据库就会被搞崩了。所以redis在某个方面相当于保护了数据库的服务器。

要解决缓存穿透,有两种方法:1.在redis中缓存空对象;优点:实现简单,维护方便;缺点:消耗redis内存。2.布隆过滤,优点:内存占用少,没有多于的key;缺点:可能会出现误判,因为布隆过滤器原理是用哈希思想来判断这个值在数据库中存不存在,但是有哈希思想就可能会存在哈希冲突。

image-20230910222317604

缓存雪崩问题及解决思路

缓存雪崩顾名思义就是Redis服务器宕机了,或者是在同一时段大量的缓存key同时失效;这两个都会让大量请求到达数据库,从而给数据库带来巨大压力。

解决方案:

  • 给不同的key的TTL(过期时间)设置随机值,只要过期时间不一样,那么就不会同时过期。
  • 利用Redis集群提高服务的可用性,既然害怕服务器宕机,那就设置一个redis集群(3主3从或者1主多从或者1主一从),这样一个服务器宕机了,其他的可以代替。
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

image-20230910223018379

缓存击穿问题及解决思路

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。常见场景如商品秒杀。

常见的解决方案有两种:

  • 互斥锁
  • 逻辑过期

解决方案一、使用锁来解决:

因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题。

假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。

image-20230910223349554

解决方案二、逻辑过期方案

方案分析:我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们内存了吗,我们可以采用逻辑过期方案。

我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个 线程去进行 以前的重构数据的逻辑,直到新开的线程完成这个逻辑后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把重建数据构建完后,其他线程才能走返回正确的数据。

这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。

1653328663897

进行对比

互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响

逻辑过期方案: 线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦