缓存雪崩

什么是缓存雪崩

由于

  • 设置缓存时,key都采用了相同expire
  • 更新策略
  • 数据热点
  • 缓存服务宕机

等原因,可能导致缓存数据同一时刻大规模不可用,或者都更新

解决方案

  • 更新策略在时间上做到比较均匀
  • 使用的热数据尽量分散到不同的机器上
  • 多台机器做主从复制或者多副本,实现高可用
  • 实现熔断限流机制,对系统进行负载能力控制

在原有失效时间基础上增加一个随机值,比如1~5分钟的随机,这样每个缓存的过期时间重复率就会降低,集体失效概率也会大大降低。

缓存穿透

什么是缓存穿透

大量并发查询不存在的KEY,导致都直接将压力透传给数据库

为什么多次透传?不存在一直为空。需要注意让缓存能够区分KEY不存在和查询到一个空值

例如:访问id=-1的数据。可能出现绕过Redis依然频繁访问数据库,称为缓存穿透,多出现在查询为null的情况不被缓存时。

解决方案

  • 缓存空值的KEY,这样第一次不存在也会被加载会记录,下次拿到有这个KEY
  • Bloom过滤或RoaringBitmap判断Key是否存在

最常见的布隆过滤器,将多有可能存在的数据哈希到一个足够大的Bitmap中,一个一定不存在的数据会被这个Bitmap拦截掉,从而避免了对底层存储系统的查询压力

  • 完全以缓存为准,
  • 使用 延迟异步加载 的策略2,这样就不会触发更新。

更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过5min。

缓存击穿

击穿是针对某一个Key缓存,而雪崩是很多key

某个Key失效时,正好有大量并发请求访问该Key

通常使用缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新,一般情况下这个种模式已经基本满足要求了

但如下问题若同时出现,可能对系统致命:

  • 为热点key,访问量非常大
  • 缓存的构建是需要时间(可能是个复杂过程,例如复杂SQL、多次I/O、多个接口依赖)

于是就会导致: 在缓存失效瞬间,有大量线程构建缓存,导致后端负载加剧,甚至可能让系统崩溃。

image-20210821140221116

解决方案

所有问题就在限制处理线程的数量,即KEY的更新操作添加全局互斥锁。

互斥锁

在缓存失效时(判断拿出来的值为空),不是立即去load db,而是

  • 先使用缓存工具的某些带成功操作返回值的操作(Redis的SETNX)去set一个mutex key
  • 当操作返回成功时,再load db的操作并回设缓存;否则,就重试整个get缓存的方法。

缓存并发

这里的并发指的是多个redis的client同时set
key引起的并发问题。其实redis自身就是单线程操作,多个client并发操作,按照先到先执行的原则,先到的先执行,其余的阻塞。当然,另外的解决方案是把redis.set操作放在队列中使其串行化,必须的一个一个执行。

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。

这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决思路:

1、直接写个缓存刷新页面,上线时手工操作下;

2、数据量不大,可以在项目启动的时候自动进行加载;

目的就是在系统上线前,将数据加载到缓存中。

如何预防缓存雪崩

在缓存的时候给过期时间在加上一个随机值,这样就会大幅度减少缓存在同一时间过期

对于“Redis挂掉了,请求全部走数据库”这种情况,我们可以有以下的思路

  • 事发前:万一Redis的高可用(采用主从架构),尽量避免Redis挂掉这种情况
  • 事发中:万一Redis真的挂了,可以设置本地缓存(ehcache) + 限流(hystrix),尽量避免数据库被干掉
  • 事发后:redis持久化,重启后自动从磁盘加载数据,快速恢复缓存数据
  1. 缓存的高可用性

    缓存层设计成高可用,防止缓存大面积故障。即使个别节点,个别机器,甚至是机房宕机,依然可以提供服务,例如Redis Sentinel 和 Redis Cluster 都实现了高可用。

  2. 缓存降级

    可以利用ehcache等本地缓存,但主要还是对源服务访问进行限流、资源隔离、降级等

    当访问量剧增、服务出现问题仍然需要保证服务还是可用的。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级,这里会涉及到运维的配合。