Skip to main content

缓存问题:穿透、击穿、雪崩

David LiuAbout 5 min

缓存问题:穿透、击穿、雪崩

图片

缓存穿透

Pass Through

概念

客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。

被动方案

  • 缓存空对象

    • 优点:实现简单,维护方便
    • 缺点:
      • 额外的内存消耗(不过可以通过 TTL 来缓解)
      • 可能造成短期的不一致
  • 布隆过滤

    • 优点:内存占用比较少,没有多余 key

    • 缺点:

      • 实现复杂(hash + bitmap + 概率实现的)

      • 存在误判可能

        说不存在一定不存在,但是说存在的时候也有一定概率是不存在的

image-20221207214912970

主动方案

  • 增强 id 的复杂度,避免被猜测 id 规律,来渗透数据库
  • 做好数据的基础格式校验(如长度,雪花算法)
  • 加强用户权限校验
  • 做好热点参数的限流 (spring cloud sentinel)

当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。

缓存雪崩

概念

同一时段大量的缓存 key 同时失效或者 Redis 服务宕机,导致大量请求到达数据库,带来巨大压力。

image-20221207221534423

解决方案

大量 key 同时过期

  • 给不同的 key 的 TTL 添加随机值

  • 后台更新缓存

    业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新

    事实上,缓存数据不设置有效期,并不是意味着数据一直能在内存里,因为当系统内存紧张的时候,有些缓存数据会被“淘汰”,而在缓存被“淘汰”到下一次后台定时更新缓存的这段时间内,业务线程读取缓存失败就返回空值,业务的视角就以为是数据丢失了。

Redis 故障宕机

  • 利用 Redis 集群提高服务的可用性(高级篇:哨兵等)

    服务熔断或请求限流机制是缓存雪崩发生后的应对方案,我们最好通过主从节点的方式构建 Redis 缓存高可靠集群

    如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务,避免了由于 Redis 故障宕机而导致的缓存雪崩问题。

  • 给缓存业务添加降级限流策略(Spring Cloud 课里面)

    为了减少对业务的影响,我们可以启用请求限流机制,只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。

  • 给业务添加多级缓存(Spring Cloud 课里面)

    • 浏览器可以添加缓存(静态数据)
    • Nginx 缓存
    • JVM 缓存

缓存击穿/热点 key 问题

概念

又称热点 key 问题,就是一个被高并发访问并且缓存重建业务较复杂(耗时较长)的 key 突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

可以发现缓存击穿跟缓存雪崩很相似,可以认为缓存击穿是缓存雪崩的一个子集。

image-20221207222348234

解决方案

  • 互斥锁

    互斥锁方案,保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。

    缺点:性能比较差,好多别的线程在旁边自旋等待

  • 逻辑过期,推荐!

    热点 key 在活动的时候,设计逻辑过期

  • 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;

image-20221207225116155

双重判断锁DCL

热Key问题可能会带来以下影响:

  • 网络拥塞。如果一个Key占用的空间很大,或者请求的命令很复杂,那么每次访问都会消耗大量的网络带宽,可能导致机器或局域网的流量被打满,影响其他服务的通信。
  • 响应时间上升、超时阻塞。由于Redis是单线程的,如果一个Key的操作耗时较长,那么就会占用Redis的CPU时间,导致其他请求等待或超时。
  • 过期删除阻塞。如果一个Key设置了过期时间,当过期时这个Key会被删除。如果这个Key很大或者很热,那么删除操作可能会阻塞Redis的服务。
  • 主从同步中断。如果一个Key很大或者很热,在主库上进行操作可能会造成主从复制的延迟或中断,影响数据的一致性和可用性。
  • 缓存穿透。如果一个Key很热,在Redis上失效或者被删除后,那么所有的请求都会直接打到后端数据库上,可能导致数据库压力过大或者崩溃。