★ 缓存穿透、缓存击穿、缓存雪崩是什么?如何应对?
结论先行:三者都是缓存失效场景下的高并发故障——穿透是"查了根本不存在的数据",击穿是"热点 key 恰好过期一瞬间被打爆",雪崩是"大量 key 同时过期或缓存整体宕机导致流量打到数据库"。应对思路分别是空值兜底与布隆过滤器、互斥锁与逻辑过期、过期时间加随机与高可用架构。
一、三兄弟对比表
| 问题 | 触发场景 | 核心危害 | 主流方案 |
|---|---|---|---|
| 穿透 | 请求查询不存在的数据 | 缓存永远不命中,DB 被打垮 | 参数校验、缓存空值、布隆过滤器 |
| 击穿 | 单个热点 key 过期瞬间 | 大量请求同时回源 | 互斥锁重建、逻辑过期、热点永不过期 |
| 雪崩 | 大量 key 同时过期或实例宕机 | 流量瞬间压垮 DB | 过期时间加随机、多级缓存、限流降级、集群高可用 |
二、典型解决方案
- 穿透:对不存在的 key 也写一个短 TTL 空值缓存;更彻底的是在缓存前加布隆过滤器,先过滤掉明显不存在的 key,注意布隆过滤器存在误判且不支持删除;
- 击穿:重建缓存时加互斥锁,只让一个线程回源写缓存,其余线程短暂等待或直接读旧值;也可用逻辑过期,把过期时间放进 value,由后台线程异步刷新,读侧短暂容忍旧数据;
- 雪崩:TTL 在基础值上加随机偏移量打散过期时刻;引入本地缓存做二级兜底;配合限流、熔断、降级保护数据库;缓存实例本身做哨兵/集群部署防止整体宕机;
- 防护次序:入口参数校验与限流在前,布隆过滤与空值缓存居中,数据库连接池与慢查询治理兜底;
- 识别配合:上线前通过监控与压测圈定热点并做缓存预热,用副本打散缓解击穿与雪崩的叠加效应。
三、互斥锁重建缓存的命令示例
# 只有抢锁成功者回源数据库并写缓存,其余请求短暂等待后重试
SET lock:hotkey 1 EX 3 NX # 抢锁,3 秒自动过期防止死锁
# 业务代码中回源 DB 后写缓存,最后释放锁
# 抢锁失败者短暂休眠后重试读缓存,避免直接打 DB
DEL lock:hotkey # 释放锁(生产用 Lua 校验持有者后删除)
四、防护层次小结
- 第一层:网关与业务入口的参数校验、黑名单与限流降级;
- 第二层:缓存侧的布隆过滤器、空值缓存、随机 TTL 与热点打散;
- 第三层:数据库侧的连接池保护与慢查询治理,作为最后兜底。
- 落地顺序:先防穿透与雪崩这类"量"的问题,再处理击穿这类"点"的问题。
常见追问 / 记忆点
- 追问:布隆过滤器能删除吗?答:标准布隆不支持删除,可用计数布隆或布谷鸟过滤器支持删除场景。
- 追问:击穿和穿透怎么区分?答:穿透是数据不存在,击穿是数据存在但缓存刚过期。
- 追问:热点数据可以永不过期吗?答:可以配合逻辑过期由后台异步刷新,用短暂不一致换取可用性。
- 记忆点:穿透查"无",击穿打"单个热点",雪崩倒"一片",应对口诀:空值布隆防穿透、加锁异步防击穿、随机 TTL 防雪崩。