一、缓存穿透(Cache Penetration)
定义
指查询一个 “不存在的数据” 时,缓存中没有(未命中),数据库中也没有,导致每次请求都穿透缓存直达数据库,形成 “空查” 风暴。
核心原因
-
数据本身不存在(如用户查询 ID=-1 的订单、不存在的商品 ID);
-
恶意攻击(如伪造大量不存在的 key 发起请求,耗尽数据库资源)。
解决方案
1. 空值缓存(最常用)
对查询结果为null
的数据,也缓存到 Redis 中,设置一个较短的过期时间(如 30 秒~5 分钟),避免同个不存在的 key 反复穿透。
-
示例:查询商品 ID=9999(不存在),数据库返回 null,Redis 缓存
key=product:9999, value=null, expire=60s
。 -
注意:过期时间不能太长,避免 “真数据” 后续新增时,缓存中的空值遮挡真实数据。
2. 布隆过滤器(防海量空 key)
当存在大量不存在的 key(如恶意攻击)时,空值缓存会占用大量 Redis 内存,此时用布隆过滤器前置拦截:
-
原理:布隆过滤器是一种空间高效的概率数据结构,存储 “数据库中已存在的 key”(如所有商品 ID、用户 ID)。查询前先过过滤器:
-
若过滤器判断 “key 不存在”,直接返回 null,不查缓存和数据库;
-
若过滤器判断 “key 可能存在”(存在极小误判率),再查缓存→数据库。
-
-
优势:内存占用极低(存储 1 亿个 key 仅需约 12MB),查询速度快(O (1))。
-
注意:存在误判率(可通过调整哈希函数数量和位数降低),且不支持删除操作(需定期重建)。
3. 业务层校验(源头拦截)
-
对输入参数做合法性校验(如用户 ID 必须为正整数、商品 ID 在合理范围),直接拦截非法请求。
二、缓存击穿(Cache Breakdown)
定义
指一个 “高频访问的热点 key” 突然过期(失效),此时大量请求同时穿透缓存直达数据库,导致数据库瞬间压力激增。
-
注意:与穿透的区别 —— 击穿是 “数据存在,但缓存刚好过期”;穿透是 “数据本身不存在”。
核心原因
-
热点 key 过期:如电商秒杀商品、热门新闻的缓存 key 到期;
-
并发量极高:同一时间大量请求访问这个刚过期的热点 key。
解决方案
1. 热点 key 永不过期(简单粗暴)
对绝对热点的 key(如首页 Banner、秒杀活动商品),不设置过期时间,由业务层主动更新(如定时任务、数据变更时同步更新缓存)。
-
风险:需确保热点 key 的数据变更能及时同步到缓存,避免数据不一致。
2. 互斥锁(分布式锁)
当缓存未命中时,通过分布式锁(如 Redis 的SET NX EX
命令)保证 “只有一个请求能去查数据库”,其他请求阻塞等待,直到数据库查询结果写入缓存后再释放锁。
-
流程:
-
请求查询 key,缓存未命中;
-
尝试获取分布式锁(
SET lock:product:100 NX EX 5
); -
若获取锁成功:查数据库→写入缓存→释放锁;
-
若获取锁失败:休眠 100ms 后重试步骤 1(直到缓存命中)。
-
-
优势:严格控制数据库访问,避免并发冲击;
-
注意:锁的过期时间需大于数据库查询时间,避免锁提前释放导致并发问题。
3. 热点 key 过期时间 “错开”
对一批热点 key(如同一类商品),设置过期时间时增加随机偏移量(如基础过期 1 小时,加上 0~10 分钟随机值),避免大量热点 key 在同一时间集体过期。
三、缓存雪崩(Cache Avalanche)
定义
指大量缓存 key 在同一时间集体过期,或 Redis 服务本身宕机,导致所有请求瞬间穿透到数据库,数据库因无法承受海量请求而宕机,形成 “缓存→数据库” 的连锁崩溃。
-
注意:与击穿的区别 —— 击穿是 “单个热点 key 过期”,雪崩是 “大量 key 同时过期或缓存服务挂了”,影响范围更大。
核心原因
-
缓存 key 集中过期:如批量导入数据时,所有 key 设置了相同的过期时间(如凌晨 2 点);
-
缓存服务故障:Redis 集群宕机、网络中断等,导致缓存整体不可用。
解决方案
1. 过期时间 “随机化”(防集中过期)
同 “缓存击穿” 的方案 3,对所有 key 的过期时间添加随机偏移量,打散过期时间点,避免集体失效。
-
示例:基础过期时间 = 3600s,实际过期时间 = 3600s + random (0, 600s)(10 分钟内随机)。
2. 缓存集群 “高可用”(防服务宕机)
通过 Redis 集群部署(如主从复制 + 哨兵模式、Redis Cluster),确保单个节点宕机时,从节点能快速切换为主节点,缓存服务不中断。
-
主从复制:主节点写入,从节点同步数据并提供读服务;
-
哨兵模式:监控主从节点,主节点宕机时自动选举从节点为主。
3. 多级缓存(降低缓存依赖)
引入 “本地缓存 + 分布式缓存” 的多级架构:
-
本地缓存(如 Java 的 Caffeine、Guava Cache):存储最核心的热点数据,访问速度更快,且不依赖 Redis;
-
分布式缓存(Redis):存储全量缓存数据。
-
当 Redis 宕机时,本地缓存可临时承接部分请求,降低数据库压力。
4. 数据库 “限流 & 降级”(最后防线)
当缓存雪崩发生时,通过限流(如用 Sentinel、Gateway 限制每秒请求数)或降级(如返回默认数据、提示 “服务繁忙”)保护数据库,避免数据库宕机。
-
示例:秒杀活动中,若 Redis 宕机,直接返回 “活动太火爆,请稍后再试”,不查数据库。
5. 缓存预热(提前加载数据)
在系统上线前或流量高峰前(如大促开始前 1 小时),通过脚本批量将热点数据加载到缓存中,避免高峰时段 “缓存未命中→查数据库” 的连锁反应。
三者对比总结
问题类型 | 触发场景 | 数据存在性 | 影响范围 | 核心解决方案 |
---|---|---|---|---|
穿透 | 查询不存在的 key | 不存在 | 单个 key 的空查 | 空值缓存、布隆过滤器、业务校验 |
击穿 | 热点 key 突然过期 | 存在 | 单个热点 key | 互斥锁、热点 key 永不过期、随机过期 |
雪崩 | 大量 key 集中过期 / 缓存宕机 | 存在 | 所有请求 | 随机过期、缓存高可用、多级缓存、限流降级 |
通过理解三者的差异,可针对性地在业务中组合使用解决方案(如 “布隆过滤器防穿透 + 互斥锁防击穿 + 随机过期 + Redis 集群防雪崩”),构建高可用的缓存架构。