缓存三剑客困难

我们来详细聊聊经典的“缓存三剑客”疑问。这是指在使用缓存(尤其是 Redis 或 Memcached)时,最常遇到且对系统危害最大的三种典型障碍:​缓存穿透、缓存击穿、缓存雪崩

理解并解决这三个障碍,是构建高可用、高性能环境的关键。


核心概念:为什么要用缓存?

首先,迅速回顾缓存的作用:为了缓解数据库(如 MySQL)的读写压力,将频繁访问的“热素材”存放在读写速度极快的内存中(缓存)。素材的查询顺序变为:​先查缓存,缓存未命中再查数据库,并将结果写入缓存

“缓存三剑客”问题就出现在这个流程的异常情况中。


第一剑:缓存穿透

1. 困难描述

缓存穿透是指查询一个数据库中根本不存在的数据。由于数据不存在,缓存中自然也不会有(缓存未命中),导致这个请求会直接穿透缓存,每次都要去数据库查询。

  • 关键特征​:数据在数据库和缓存中都不存在。

  • 危害​:若是有人恶意发起大量这类请求(比如用不存在的用户ID查询用户信息),会瞬间给数据库带来巨大压力,甚至导致数据库宕机。

2. 解决方案
  1. 缓存空对象

    • 做法​:即使从数据库没查到,也向缓存中写入一个空值(如 null),并设置一个较短的过期时间(例如 3-5 分钟)。

    • 优点​:搭建简单,能有效应对短期的大量攻击。

    • 缺点​:可能会在缓存中存储大量无意义的空键,占用内存;可能存在短期数据不一致(比如资料后来被录入了,但缓存里还是空值)。

  2. 布隆过滤器

    • 做法​:在缓存之前,设置一个布隆过滤器。布隆过滤器是一个高效的数据结构,用于快速判断“某个元素一定不存在”或“可能存在”于某个集合中。

    • 流程​:

      1. 将所有可能查询的数据的 key 哈希后映射到布隆过滤器的位数组中。

      2. 请求来时,先经过布隆过滤器判断 key 是否存在。

        • 如果不存在,则直接返回空,拒绝访问数据库。

        • 如果存在,才继续后续的缓存查询流程。

    • 优点​:内存占用极小,能从根本上彻底解决穿透问题。

    • 缺点​:实现稍复杂;有误判率(“可能存在”意味着它可能会把一些合法的、但不在过滤器里的新 key 误判为不存在,但不会误判存在的素材为不存在);数据变更时维护布隆过滤器较麻烦。


第二剑:缓存击穿

1. 问题描述

缓存击穿是指一个访问非常频繁的“热点数据”​​(比如某明星的微博)在缓存过期(失效)的瞬间。由于这个 key 可能被大量并发请求访问,在它失效的瞬间,所有对这些数据的请求都会穿透缓存,直接打到数据库上,仿佛缓存被“击穿”了一个洞。

  • 关键特征“热点”。就是​:数据存在,但缓存刚好过期。key

  • 危害​:在热点 key 失效的瞬间,巨大的并发可能压垮数据库。

2. 解决方案
  1. 设置热点数据永不过期

    • 做法​:对于极热点的 key,可以不对其设置过期时间。随后通过后台任务或程序逻辑,在数据更新时主动刷新缓存。

    • 优点​:简单,一劳永逸。

    • 缺点​:需要人工识别热点内容;信息一致性应该靠逻辑维护。

  2. 互斥锁

    • 做法​:当缓存失效时,不立即去查询数据库。而是先尝试获取一个分布式锁(如用 Redis 的 SETNX命令)。只有一个线程能成功获取锁,这个线程负责去查询数据库并重建缓存。其他未获取到锁的线程则等待一段时间后重试查询缓存。

    • 优点​:能很好地保护数据库,逻辑严谨。

    • 缺点​:搭建复杂;如果获取锁的线程挂掉,可能得处理锁超时;性能上有一定损耗(等待)。

  3. 逻辑过期

    • 做法​:不给缓存数据设置物理过期时间,而是在缓存 value 中额外存储一个逻辑过期时间。当业务线程发现数据逻辑上已过期时,它不会立即重建缓存,而是尝试获取互斥锁。拿到锁的线程会启动一个新线程去异步重建缓存,而自己则返回旧的、已过期的数据。其他线程在锁被占用期间,也直接返回旧数据。

    • 优点​:性能极佳,用户无感知,永远有数据返回。

    • 缺点​:实现最复杂;会有一段时间的数据延迟(返回旧数据)。


第三剑:缓存雪崩

1. 问题描述

缓存雪崩是指缓存中大量的 key 在同一时间点或时间段内集中失效,或者缓存服务直接宕机。导致所有原本应该访问缓存的请求,瞬间全部涌向数据库,数据库无法承受巨大的压力而崩溃,进而导致整个系统崩溃,就像雪崩一样。

  • 关键特征​:大量 key 同时失效 或 缓存服务不可用。

  • 与击穿的区别​:击穿是单个热点 key 失效,雪崩是大量 key 同时失效。

2. 解决方案
  1. 设置随机的过期时间

    • 做法​:在为缓存数据设置过期时间时,在基础过期时间上加上一个随机值(如 1-5 分钟的随机数)。这样可以让 key 的过期时间尽量分散,避免同时失效。

    • 优点预防雪崩的首选方案。就是​:简单有效,

  2. 构建高可用的缓存集群

    • 做法​:通过 Redis 的哨兵模式或集群模式,建立缓存服务的高可用。即使个别节点宕机,整个集群仍然可以提供服务。

    • 目的​:防止因缓存服务宕机而引发的雪崩。

  3. 服务熔断与降级

    • 做法​:当应用系统检测到数据库压力过大或响应过慢时,启动熔断机制,暂时停止访问数据库,直接返回预设的默认值(如“系统繁忙,请稍后再试”)或兜底数据。给数据库“止血”,等缓存服务恢复后,再关闭熔断。

    • 目的​:牺牲部分用户体验和非核心功能,保证核心业务和系统整体不崩溃。

  4. 持久化存储预热

    • 做法​:在缓存服务重启或大规模失效后,架构正式对外提供服务前,先通过一个脚本或程序,将高频访问的数据提前加载到缓存中。


总结与对比

问题类型

核心原因

关键特征

主要解决方案

缓存穿透

查询不存在的数据

数据库和缓存中都没有

1. 缓存空对象
2. 布隆过滤器

缓存击穿

热点 key​ 突然过期

单个热点 key 失效,并发高

1. 永不过期
2. 互斥锁
3. 逻辑过期

缓存雪崩

大量 key​ 同时失效或缓存宕机

大规模缓存失效,框架级故障

1. 设置随机过期时间
2. 缓存高可用集群
3. 服务熔断与降级

应对法则​:

  1. 防穿透否合法。就是​:守住第一道门,判断请求

  2. 防击穿​:保护热点,避免单点并发。

  3. 防雪崩​:分散风险,保证服务可用。

在实际计划中,通常需要组合运用这些策略,才能构建一个健壮的缓存系统。

posted @ 2026-01-25 19:09  yangykaifa  阅读(0)  评论(0)    收藏  举报