Jonsson Yan' Blog

怕什么真理无穷,进一步有进一步的欢喜。

0%

缓存雪崩、缓存穿透、缓存击穿

缓存雪崩

出现的原因

缓存雪崩出现的原因是当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到 DB 上,这样可能导致整个系统的崩溃。

解决方案

  1. 设置不同的过期时间

比较简单且好理解的方案是我们在缓存的过期时间上设置一个 1-5min 的随机值,这样每个缓存的过期时间重复率就会降低,就可以避免缓存在某一时刻大规模到期的情况。

  1. 针对热 key 设置永不过期

设置热 key 永不过期或者针对即将过期的热 key 使用异步线程不断的刷新过期时间。

  1. 限流,使用锁或者队列,避免同时刻大量请求打崩 DB

加锁或者队列的方式保证缓存的单线程写,这样可以避免缓存大量失效后,请求短时间内都打到 DB 上。缺点是系统的吞吐率降低。

  1. 分级缓存

缓存击穿

出现的原因

缓存击穿和缓存雪崩比较相似,缓存雪崩是 key 大部分失效,而缓存击穿式热点 key 失效。一个设置了过期时间的 key 在某个时间大并发直接请求,并且此时正好过期的情况下,请求在缓存中没有读取到数据就会直接打到 DB 上,导致服务崩溃。

解决方案

  1. 加锁更新

比如请求查询 A,发现缓存中没有,对 A 这个 key 加锁,同时去数据库查询数据,写入缓存,再返回给用户,这样后面的请求就可以从缓存中拿到数据了。

  1. 针对热 key 设置永不过期

  2. 限流,使用锁或者队列,避免同时刻大量请求打崩 DB

缓存穿透

出现的原因

根本原因是请求了缓存中不存在的 key,导致每次请求都打到 DB 上。通常处于系统容错考虑,我们会在查询后如果有结果则写入到缓存,这样下一次请求时就可以直接从缓存中读取数据。然后当请求的 id 在数据库中存在时,比如数据库中主键使用的自增 id,而请求的是-1,那么这将导致缓存中不存在这个数据而每次请求都会从数据库中查,这就失去了缓存的意义,当并发量很大时会导致系统宕机。

解决方案

  1. 虽然从 DB 中查到的空,但是仍然把数据写到缓存

这是最简单粗暴的方法,不管从 DB 中查到的是什么都会写入到缓存,缓存有效期设置短些即可,比如 <=5min。

  1. 使用布隆过滤器

布隆过滤器的原理是在你存入数据的时候,会通过散列函数将它映射为一个位数组中的 K 个点,同时把他们置为 1。当用户来查询 A,而 A 在布隆过滤器值为 0,直接返回,就不会打到 DB 了。使用布隆过滤器之后会有一个问题就是误判,因为它本身是一个数组,可能会有多个值落到同一个位置,那么理论上来说只要我们的数组长度够长,误判的概率就会越低。

  1. 做好参数校验

拦截非法的参数就可以减少 DB 中查不到数据的情况。