学习笔记—缓存穿透、缓存雪崩、缓存击穿

发布时间 2023-12-07 17:08:53作者: 吧拉吧拉吧

缓存穿透

  • 定义:缓存穿透指的查询缓存和数据库(缓存找不到就会在数据库找)中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在一样。
    • 例:“当查询数据库时如果没有查询到数据,则直接返回Null给前端用户,流程结束”,如果前端频繁发起访问请求时,恶意提供数据库中不存在的Key,则此时数据库中查询到的数据将永远为Null。由于Null的数据是不存入缓存中的,因而每次访问请求都会查询数据库。如果此时有恶意攻击,发起“洪流”式的查询,则很有可能会对数据库造成极大的压力,甚至压垮数据库。
  • 缓存穿透可能会使后端存储负载加大,如果发现大量存储层空命中,可能就是出现了缓存穿透问题。
  • 解决方法
    1. 缓存空值:在数据库不命中之后,把这个Key值保存进Redis,值设为空对象或者默认值,当下次再通过这个Key查询时就不需要再查询数据库。
      1. 问题:空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间。解决方法:设置一个过期时间
      2. 问题:假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。
    2. 使用布隆过滤器,可以在缓存之前再加一层布隆过滤器,在查询的时候先去布隆过滤器查询 key 是否存在,如果不存在就直接返回。
      1. 步骤:
        1. 第一次初始化的时候,会把数据库中所有已存在的key,经过一些列的hash算法(比如:三次hash算法)计算,每个key都会计算出多个位置,然后把这些位置上的元素值设置成1。
        2. 当有用户key请求过来的时候,再用相同的hash算法计算位置。如果多个位置中的元素值都是1,则说明该key在数据库中已存在。这时允许继续往后面操作。如果有1个以上的位置上的元素值是0,则说明该key在数据库中不存在。这时可以拒绝该请求,而直接返回。
      2. 问题:
        1. 存在误判的情况。但通常情况下,布隆过滤器的误判率还是比较少的。即使有少部分误判的请求,直接访问了数据库,如果访问量并不大,对数据库影响也不大。
          • 解决方法:可以适当增加hash函数
        2. 存在数据更新问题。如果数据库中的数据更新了,需要同步更新布隆过滤器。但它跟数据库是两个数据源,就可能存在数据不一致的情况。

缓存击穿

  • 定义:缓存中某个频繁被访问的Key(可以称为“热点Key”),在不停地扛着前端的高并发请求,当这个Key突然在某个瞬间过期失效时,持续的高并发访问请求就“穿破”缓存,直接请求数据库,导致数据库压力在某一瞬间暴增。这种现象就像是“在一张薄膜上凿出了一个洞”。
  • 例:很多用户同时购买一个商品,这个商品本来是放在缓存里面,突然某个时刻这个商品的缓存过期了,但是这个时候还有很多用户在购买这个商品,这个时候这些用户的请求就还打在数据库上,一下子数据库的压力过大,就可能直接挂掉。
  • 解决方法:
    • 加锁:如上例中,查询缓存,发现缓存中不存在,加锁,让其它线程等待,只让一个线程去更新缓存。
    • 缓存不失效:如上例中,大量用户抢购一个商品,就相当于一个热门商品的秒杀活动,可以不用设置过期时间,让其永久有效的。然后,在秒杀活动开始前,我们先用一个程序提前从数据库中查询出商品的数据,然后同步到缓存中,提前做预热。等秒杀活动结束一段时间之后,我们再手动删除这些无用的缓存即可。

缓存雪崩

  • 定义:当某一个时刻出现大规模的缓存失效的情况 (例如缓存服务宕机、大量key在同一时间过期) ,那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。这时候如果运维马上又重启数据库,马上又会有新的流量把数据库打死。这就是缓存雪崩。
  • 分析:这种问题产生的原因其实主要是因为大量的Key在某个时间点或者某个时间段过期失效导致的。
  • 解决方法:
    • 过期时间:为这些Key设置不同的、随机的TTL(过期失效时间),从而错开缓存中Key的失效时间点,可以在某种程度上减少数据库的查询压力。而热点数据可以设置永不过期
    • 提高缓存可用性:集群部署、多级缓存
    • 熔断降级:
      • 服务熔断
      • 服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。

分析总结

  • 不管是缓存穿透、缓存雪崩还是缓存击穿,最终的后果都是给DB(数据库)造成压力,甚至压垮数据库。那么它们的解决方案也都有一个共性,那就是“加强防线”,尽量让高并发的读请求落在缓存中,从而避免打到数据库。
  • 缓存雪崩可以说是缓存击穿的升级版,缓存击穿只是一个热门的key突然失效,而缓存雪崩是多个热门的key同时失效。

参考:
[1] 分布式中间件技术实战-java版- 钟林森
[2] https://cloud.tencent.com/developer/article/2046488