缓存穿透、缓存击穿、缓存雪崩的区分与完整解决方案
2026/6/13 1:07:03 网站建设 项目流程

一、核心区分

问题

核心特征

触发场景

简单理解

缓存穿透

查不存在的数据,缓存 + DB 都无数据,请求直打 DB

非法 ID、恶意遍历、空数据查询

穿过去,两边都没数据

缓存击穿

热点 Key 突然失效,大量并发同时打向 DB

秒杀商品、首页热点、固定热点缓存过期

一个热门 Key 崩了,并发扎进 DB

缓存雪崩

大量 Key 集体失效 / 缓存服务宕机,全量请求压垮 DB

缓存同一时间批量过期、Redis 集群挂掉

整片缓存全挂,流量全部冲数据库

二、详细讲解 + 对应解决方案

一、缓存穿透

1. 成因

查询数据库根本不存在的数据:

  • 前端传非法 ID(负数、超长 ID、随机乱 ID)
  • 爬虫 / 恶意攻击遍历 ID(如id=1,2,3...批量扫库)
  • 业务空数据(用户已删除、订单已销毁)

链路:请求 → 缓存未命中 → 查询数据库 → 无数据 → 不写缓存 → 下次请求重复走 DB。
高并发下DB 压力持续暴涨

2. 解决方案(按推荐优先级)

方案 1:空值缓存(最简单,业务首选)

  • 逻辑:DB 查询为空时,依然往缓存写入一个空标记,设置较短过期时间(5~10 分钟)。
  • 优点:实现简单、防穿透效果明显
  • 缺点:会占用少量缓存空间,极端恶意请求会产生大量空 key

java
// 伪代码
String key = "order:" + orderNo;
String cacheVal = redis.get(key);
if (cacheVal != null) {
return "".equals(cacheVal) ? null : cacheVal;
}
// 查询DB
Order order = db.select(orderNo);
if (order == null) {
redis.set(key, "", 600); // 空值,10分钟过期
return null;
}
redis.set(key, JSON.toJSONString(order), 3600);
return order;

方案 2:布隆过滤器(高并发、海量数据推荐)

  • 原理:把所有合法业务 ID提前存入布隆过滤器,请求先过过滤器。
  • 流程:请求 ID → 布隆过滤器判断不存在→ 直接拦截,不查缓存与 DB。
  • 适用:订单、用户、商品等固定合法数据集
  • 特点:
  • 优点:彻底拦截非法请求,性能极高
  • 缺点:存在误判(不会漏判);数据新增 / 删除需要维护过滤器;不适合频繁变动数据

方案 3:接口层参数校验(前置拦截)

  • 限制 ID 规则:ID 必须正数、长度范围、格式校验
  • 黑名单、IP 限流:恶意 IP 直接封禁
  • 适用:网关 / 拦截器统一前置,从源头减少无效请求

方案 4:Redis 布谷鸟过滤器(进阶)

弥补布隆过滤器不能删除的问题,适合数据频繁增删场景。

二、缓存击穿

1. 成因

单个热点 Key到期失效,瞬间海量并发请求同时绕过缓存,直达数据库。
典型场景:秒杀商品、爆款商品、首页活动、高频查询订单。

关键点只有一个热点 Key 失效,不是批量失效。

2. 解决方案(按推荐优先级)

方案 1:互斥锁(分布式锁,通用稳定)

主流:Redis 分布式锁,保证同一时刻只有一个请求查 DB + 回写缓存,其余请求等待重试。

  • 流程:
  1. 缓存失效
  1. 尝试加分布式锁,抢到锁 → 查询 DB → 写入缓存 → 释放锁
  1. 未抢到锁 → 短暂休眠后重试读缓存
  • 优点:通用、可靠,几乎无业务侵入
  • 缺点:并发极高时会有少量等待,影响一点吞吐量

java
// 伪代码(Redis 分布式锁)
String key = "hot:goods:1001";
String lockKey = "lock:hot:goods:1001";
String val = redis.get(key);
if (val != null) {
return val;
}
// 尝试加锁
boolean lock = redis.tryLock(lockKey, 30);
if (lock) {
try {
// 查询DB
String dbData = db.query();
redis.set(key, dbData, 3600);
return dbData;
} finally {
redis.unLock(lockKey);
}
} else {
// 短暂休眠后重试
Thread.sleep(50);
return redis.get(key);
}

方案 2:永不过期(热点 Key 专用,最优)

绝对热点、数据更新不频繁的 Key,不设置过期时间

  • 手动更新 / 后台定时任务更新缓存,杜绝过期击穿。
  • 适用:首页配置、爆款商品、基础配置数据。
  • 优点:零击穿风险、性能最高
  • 缺点:数据更新存在延迟;内存占用持续存在

方案 3:热点 Key 逻辑永不过期(续期)

代码层面判断即将过期,后台异步主动刷新,前端无感知。

方案 4:多级缓存(本地缓存 + Redis)

Caffeine/Guava 本地缓存 + Redis 二级缓存。
热点数据留在 JVM 本地,请求根本不打到 Redis,彻底规避击穿。

  • 优点:性能拉满
  • 缺点:多实例存在数据不一致,适合允许短时间不一致的场景

三、缓存雪崩

1. 两类成因(必须分清)

类型 A:大量缓存 Key 同时过期

批量 Key 设置了相同过期时间,同一时刻集体失效,流量全压 DB。

类型 B:Redis 服务整体宕机 / 不可用

Redis 集群断电、主从故障、网络分区,所有缓存全部不可用,流量直接冲垮 DB。

击穿是单点热点 Key,雪崩是整片缓存

2. 解决方案(分两类场景)

场景 A:解决「批量 Key 同时过期」

方案 1:过期时间加随机值(最简单,必用)

原有过期时间 +随机偏移量(1~10 分钟),打散过期时间,避免集体失效。

java
// 原 3600秒,增加 0~600 随机秒
int expire = 3600 + new Random().nextInt(600);
redis.set(key, value, expire);

  • 优点:零成本、线上直接改,基础兜底方案

方案 2:分层过期、错峰更新

按业务模块划分不同过期时间,避免全模块同时刷新。

场景 B:解决「Redis 集群宕机」(核心高可用)

方案 1:Redis 高可用架构(基础保障)

  • 主从 + 哨兵(Sentinel):主节点挂掉自动切换从节点
  • Redis Cluster 集群:分片 + 多副本,部分节点故障不影响整体
    生产环境强制部署,不能单机 Redis

方案 2:多级缓存降级(本地缓存兜底)

JVM 本地缓存(Caffeine)作为二级缓存。
Redis 挂了,请求走本地缓存,保护 DB

  • 注意:接受多实例短暂数据不一致。

方案 3:服务熔断 & 限流 & 降级(网关 / 层防护)

结合 Sentinel / Resilience4j / Hystrix:

  • 限流:限制每秒请求总量,防止流量打爆 DB
  • 熔断:DB 异常时直接熔断,不再向下游请求
  • 降级:返回默认值、静态页面、提示文案,放弃实时数据

方案 4:缓存预热

系统启动、大促前,提前批量加载热点数据进缓存,避免冷启动雪崩。

三、三者对比总结

  1. 缓存穿透
  • 问题:查不存在的数据
  • 典型:非法 ID、爬虫遍历
  • 首选方案:空值缓存 + 参数校验;海量数据用布隆过滤器

2.缓存击穿

  • 问题:单个热点 Key 过期,并发打 DB
  • 典型:秒杀、爆款商品
  • 首选方案:分布式锁 / 热点 Key 永不过期

3.缓存雪崩

  • 问题:大量 Key 同时过期 或 Redis 整体挂掉
  • 典型:统一过期时间、Redis 宕机
  • 首选方案:过期加随机值 + Redis 高可用 + 限流熔断降级

四、电商 / 支付业务落地组合方案

  • 全链路前置:网关参数校验 + IP 限流(防穿透源头)
  • 防穿透:空值缓存(5 分钟过期)+ 布隆过滤器(订单 ID 集合)
  • 防击穿:支付热点订单 / 爆款商品 →Redis 分布式锁 + 热点 Key 不主动过期
  • 防雪崩:所有缓存过期时间加随机偏移
  • Redis 部署 Cluster 集群 + 哨兵
  • 接入 Sentinel 做限流、熔断、降级

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询