缓存穿透、击穿、雪崩:三个总被混为一谈的故障

简介: 「缓存击穿和雪崩有什么区别」十个人六个答不上来,不是因为难,是这两个词在国内流传中被混用了。本文先把定义钉死,再给每种故障的首选解法与兜底手段。

缓存穿透、击穿、雪崩:三个总被混为一谈的故障

面试里被问「缓存击穿和雪崩有什么区别」,十个人里有六个答不上来——不是因为难,是因为这两个词在国内的流传过程中被混用了。

这篇先把定义钉死,再给解法。

先分清:查询的三种「没拿到」

所有缓存故障,都可以还原成一个问题:一次读请求没拿到想要的数据,它去哪了?

| | 故障 | 数据存不存在 | 缓存里有没有 | 请求打到哪

| 穿透 | 不存在 | 从来没有 | 每次都打到 DB
| 击穿 | 存在 | 恰好过期 / 被删 | 瞬间全部打到 DB
| 雪崩 | 存在 | 大面积失效 | 大量请求打到 DB

一句话记忆:

  • 穿透 = 查的东西压根没有,缓存挡不住,因为它不知道你要什么
  • 击穿 = 查的东西一直有,缓存刚好失效,那一个瞬间挡不住
  • 雪崩 = 查的东西一直有,缓存整体垮了,谁都挡不住

命名上要留意:击穿在英文里是 Cache Breakdown,也常被叫 Hotspot Invalidation;雪崩是 Cache Avalanche。但很多团队的文档里这两个是互换的。面试时先问对方「你们怎么定义」,比背定义有用。


一、缓存穿透:查不存在的数据

现象

攻击者用一个不存在的 ID 列表刷你的接口:/user?id=1、/user?id=2……

每个请求都是:查缓存 → 未命中 → 查 DB → 未找到 → 不写缓存。DB 被打满,而缓存里一个字节都没多。

更隐蔽的版本:某个字段线上是 null,比如 user.phone,大量用户没填。你查了 100 万次 null,缓存永远是空的。

解法

① 缓存空值

查不到也写进去,TTL 设短(30 秒到 5 分钟):

User u = cache.get(key);
if (u == null) {
   
    u = db.query(id);
    if (u == null) {
   
        cache.set(key, NULL_PLACEHOLDER, 60);  // 关键
    } else {
   
        cache.set(key, u, 1800);
    }
}

简单有效,但拦不住「攻击者每次换一个不存在的 ID」——空值缓存只对重复查询同一个 key 有效。

② 布隆过滤器

启动时把所有合法 ID 灌进布隆位图。查询时先问它:

if (!bloom.mightContain(id)) {
   
    return NOT_FOUND;   // 一定不存在,直接返回,不碰 DB
}

它有假阳性(说「可能在」但其实不存在),但不会有假阴性(说「不在」就是真不在)。所以它适合做第一道拦截,把绝大多数恶意请求挡在 DB 之外。

代价:位图有空间开销,且删除元素困难(需要 Counting Bloom 或定期重建)。

③ 参数校验

最朴素也最容易被忽略:id 是不是正数、是不是合法格式。不合法直接 400,别往下传。


二、缓存击穿:一个热点 key 失效的瞬间

现象

有个爆款商品的 key,QPS 一万。平时缓存命中,风平浪静。

某天这个 key 到了 TTL,或者被人手动删了。接下来 1 秒内,一万个请求同时发现缓存没了,同时去查 DB,同时试图写回。

DB 被打出一个尖峰,然后可能就挂了。

解法

① 互斥锁(single flight)

只让第一个线程去查 DB,其他线程等它查完再读缓存:

Object lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
if (lock.tryLock(0, TimeUnit.MILLISECONDS)) {
   
    try {
   
        // 双重检查:等锁期间可能别人已经填好了
        Object v = cache.get(key);
        if (v == null) {
   
            v = loadFromDB(key);
            cache.set(key, v);
        }
        return v;
    } finally {
   
        lock.unlock();
        locks.remove(key);   // 必须移除,否则内存泄漏
    }
} else {
   
    // 没抢到锁,短暂自旋后重读缓存
    return retryReadCache(key);
}

两个坑:锁一定要在 finally 里 remove,否则 key 多了锁表也跟着涨;另外要双重检查,因为你等锁的这段时间里,别人可能已经把值填进缓存了。

② 逻辑过期

不给 key 真的设 TTL,而是在值里塞一个 expireAt 时间戳:

Record {
    V value; long expireAt; }

V v = cache.get(key);
if (v != null) {
   
    if (v.expireAt > now()) return v.value;
    // 逻辑上过期了,但物理上还在,直接返回旧值
    asyncRefresh(key);        // 后台异步刷新,不阻塞
    return v.value;
}

好处是永远不会出现「全部同时失效」——刷新是异步的,一个一个来。代价是牺牲了短暂的数据新鲜度,换可用性。在电商大促这种场景,这个取舍很划算。


三、缓存雪崩:整体垮掉

现象

两种触发方式:

  • 大量 key 同一时刻过期。比如批量预热时 TTL 都设成了 3600 秒,那这一批就在整点同时炸。
  • 缓存服务本身挂了。Redis 主节点故障、网络分区、连接池打满。

区别在于:击穿是一个 key,雪崩是一大片。

解法

① 过期时间打散

不要所有 key 用同一个 TTL。加随机:

long ttl = baseTtl + ThreadLocalRandom.current().nextLong(0, 600);  // 0~10min 抖动
cache.set(key, value, ttl);

一行代码,把「同时过期」变成「陆续过期」。

② 多级缓存

本地缓存 + Redis。本地缓存在 Redis 挂掉时还能扛一波:

// L1:进程内 Caffeine,TTL 短
V v1 = localCache.get(key);
if (v1 != null) return v1;

// L2:Redis
V v2 = redis.get(key);
if (v2 != null) {
    localCache.put(key, v2, 30, SECONDS); return v2; }

// 都 miss,走 DB
V v3 = loadFromDB(key);
localCache.put(key, v3, 30, SECONDS);
redis.set(key, v3);
return v3;

代价是一致性问题被放大:本地缓存里的数据在多台机器上不一致,容忍窗口最长 30 秒。业务上能不能接受这个窗口,是选型的前提。

③ 熔断降级

Redis 挂了不要让请求全部堆到 DB 上。挂个熔断器,DB 的 QPS 超过阈值就快速失败:

if (!circuitBreaker.tryAcquire()) {
   
    return DEGRADED_RESPONSE;   // 返回兜底数据,而不是拖死 DB
}

宁可返回「稍后重试」,也不要把 DB 一起拖下水。

④ 随机过期 + 提前预热

Redis 重启后是空的,那一刻所有流量都会打到 DB。所以:重启前预热常用数据;给每个节点的缓存 TTL 加随机偏移,避免所有节点同时冷启动。


三个故障的对照表

| | | 穿透 | 击穿 | 雪崩

| 本质 | 查不存在的 | 单点失效 | 大面积失效
| 触发 | 恶意/异常参数 | 热点 key 过期 | 批量过期/服务挂
| 首选解 | 布隆过滤器 | 互斥锁/逻辑过期 | TTL 打散 + 多级缓存
| 兜底 | 空值缓存 | 双重检查 | 熔断降级
| 监控指标 | 缓存 miss 率 | 单 key 的 DB 回源 QPS | 整体命中率断崖

最后一句

这三个问题的共同点是:请求绕过缓存直接打到 DB,而 DB 是整条链路上最脆弱的一环。

所以设计缓存时不要只想着「命中率」,要想「最坏情况下会发生什么」。缓存系统的设计难度,从来不在正常路径上。

相关文章
|
22天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9166 29
|
20天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
4370 16
|
8天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
20天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2387 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
6天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
548 1
|
1天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
151 120
|
9天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
1107 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
|
13天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
1607 0
|
3天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
321 0

热门文章

最新文章