掘金 后端 ( ) • 2024-04-09 15:29

最近建了一个技术交流群,欢迎志同道合的同学加入,群里主要讨论:分享业务解决方案、深度分析面试题并解答工作中遇到的问题,同时也能为我提供写作的素材。

群号 208236931,欢迎进群交流学习,一起进步、进步、进步!

前言

在实际的工作中,redis作为缓存使用,降低DB的压力,应用比较广泛。老生常谈的话题,什么是缓存击穿、缓存穿透、缓存雪崩,遇到了应该怎么去解决?

我想说的是,大部分人其实都不会遇到这个问题,为什么呢?不管你做C端,还是B端,流量不到一定程度,根本不会遇到。你想想是不是,或者你真遇到过吗?我目前为止,也就遇到过,缓存穿透,那还是有人恶心访问导致的。

但是这些概念,以及解决的方案,还是需要去了解下,为什么呢?面试的时候,90%的会被问。最主要是深入了解下,提升自己。假设哪天泼天富贵的流量砸到你身上,起码地主家有存粮,心里不慌,轻松拿捏。

下面我们一起了解下,什么是缓存穿透、缓存雪崩、缓存穿透,出现的原因,以及解决方案。

缓存穿透

当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存中数据不存在,再去访问数据库,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。

产生的原因

  • 查询不存在的数据:当查询一个不存在于缓存中的数据时,缓存无法命中,请求会直接访问数据库。
  • 恶意请求:恶意请求会通过各种方式绕过缓存,直接访问数据库。

解决方案

  • 布隆过滤器(Bloom Filter):使用布隆过滤器可以在缓存层面过滤掉一些无效的请求,从而避免无效请求直接访问数据库。
    • 布隆过滤器是一种高效的数据结构,可以快速判断某个元素是否存在于一个集合中。通过在缓存层面使用布隆过滤器,可以在查询前快速判断数据是否存在,如果不存在可以直接返回结果,避免对数据库的查询操作。
  • 缓存空值处理:当查询的数据在数据库中确实不存在时,可以将空值也缓存起来,设置一个较短的过期时间。这样,下次查询同样的数据时,就可以从缓存中获取到空值,避免再次访问数据库。

缓存雪崩

缓存雪崩指的是在某个时间点,缓存中的大部分或全部数据同时失效,导致大量的请求直接落到数据库上,从而引发数据库的压力过大,甚至崩溃。这种情况通常发生在缓存中的数据在同一时间段内过期,或者由于某种原因导致缓存失效。

产生的原因

  • 缓存过期时间设置不合理:如果大量的缓存在同一时间段内过期,就会导致大量请求直接访问数据库。
  • 缓存服务器故障:当缓存服务器发生故障,无法提供服务时,所有的请求都会直接访问数据库。
  • 热点数据集中:如果某些热门数据的缓存失效时间相近,可能会导致大量请求同时访问数据库。

解决方案

  • 设置合理的缓存过期时间:合理设置缓存的过期时间,避免大量缓存同时失效。
  • 分散缓存失效时间:将热门数据的缓存失效时间分散开,避免集中在同一时间段失效,失效时间:固定的时间+一个随机值
  • 实现缓存高可用:通过使用缓存集群和备份服务器等机制,提高缓存的可用性,减少单点故障的风险。

缓存击穿

缓存击穿指的是一个热点数据的缓存失效,导致大量请求直接落到数据库上,造成数据库压力过大,影响系统性能。与缓存雪崩不同,缓存击穿指的是某个特定的数据失效,而不是全部数据。

产生的原因

  • 热点数据缓存失效:当某个热点数据的缓存失效时,大量请求同时访问数据库。
  • 高并发请求:在高并发的情况下,大量请求同时访问数据库,容易造成缓存击穿。

解决方案

  • 如果是热点数据,那么可以考虑设置永远不过期。
  • 互斥锁(Mutex):在缓存失效的时候,可以使用互斥锁来控制只有一个请求可以访问数据库,其他请求需要等待。当名列前茅个请求重新生成缓存后,其他请求可以从缓存中获取数据,避免对数据库的重复访问。
  • 预加载(Cache Preloading):在缓存失效前,通过定时任务或者后台线程提前加载热点数据到缓存中,避免热点数据缓存失效时的突然访问峰值。
  • 分布式锁(Distributed Lock):使用分布式锁的机制,确保只有一个线程或者进程可以进行缓存重建操作。其他线程或者进程在获取锁之前需要等待,避免重复重建缓存。
  • 降级策略(Fallback Strategy):在缓存失效的情况下,可以提供一个备用方案,例如直接返回默认值或者从数据库中获取数据。这样可以保证系统的稳定性,避免由于缓存击穿导致的系统崩溃。

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存。

如果不进行预热, 那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。

缓存预热的操作方法

  • 数据量不大的时候,工程启动的时候进行加载缓存动作;
  • 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
  • 数据量太大的时候,优先保证热点数据进行提前加载到缓存。

场景

这种场景,非常多的,比如:

  • 抢购
  • 秒杀
  • 新系统上线
  • 公告
  • 热点新闻