没有“一套通用防护体系”能同时治好击穿、穿透、雪崩,因为三者触发机制不同:穿透是查不存在的key导致缓存层无响应,击穿是单个热key过期瞬间并发冲击,雪崩是大量key集中失效或Redis整体宕机;强行统一方案会引发新问题,如布隆过滤器对击穿无效,随机过期时间可能使热点key提前失效诱发击穿。

直接说结论:没有“一套通用防护体系”能同时治好击穿、穿透、雪崩,因为三者触发机制不同,强行套同一套方案反而会引入新问题——比如用布隆过滤器防穿透,对击穿毫无作用;给所有 key 加随机过期时间防雪崩,却可能让热点 key 提前失效引发击穿。
缓存穿透:查不存在的 key 时数据库被反复打穿
典型现象是 cache miss rate 突然飙到 95% 以上,但 db qps 暴涨,而慢查询日志里全是类似 SELECT * FROM user WHERE id = -1 这种明显非法的请求。
核心思路不是“拦住所有非法请求”,而是“让缓存层对不存在的 key 也有响应能力”:
- 优先用
cache null value:查不到就往 Redis 写个空标记(如"null"或自定义NullValue对象),设短过期(300秒足够),避免长期占内存 - 慎用布隆过滤器:它适合写少读多、key 总量可预估的场景(如用户 ID 全集已知);但对动态生成的 ID(如订单号含时间戳)、或业务频繁增删 key 的情况,误判率会上升,且维护成本高
- 别把空值缓存和业务逻辑混在一起:比如用
""表示空,结果某个真实业务字段恰好是空字符串,就会误判;建议用独立类型(如 Java 的NullValue类)或固定前缀(如"__NULL__")
缓存击穿:单个热 key 过期瞬间流量全打库
关键指标是监控看到某个 redis key 的 ttl 归零后,对应数据库表的 select QPS 在 1–2 秒内冲高 50 倍以上,且只集中在一条 SQL 上。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
不能靠延长过期时间解决——热点 key 本就该及时更新;重点是“错峰回源”:
- 用分布式锁(如
SET key val NX EX 3):第一个请求拿到锁去查库、写缓存;其余请求等锁释放后直接读缓存。注意锁超时必须短于业务查询耗时,否则会卡死 - 逻辑过期(推荐):缓存值里额外存一个
expireAt时间戳,过期不删 key,而是后台异步刷新;读时发现逻辑过期,只开一个线程去 reload,其余请求仍返回旧值——牺牲一点一致性换可用性 - 别在应用层做本地锁:单机锁对集群无效;Redis 的
SETNX是唯一可靠基座,不要自己用synchronized或ReentrantLock
缓存雪崩:大面积 key 同时失效或 Redis 整体不可用
典型信号是 Redis evicted_keys 或 expired_keys 指标在某一分钟内激增,同时数据库连接池 active connections 打满,且错误日志出现大量 Connection refused 或 timeout。
本质是“缓存层集体失能”,防御要分两层:
- 预防集中失效:给同类 key 的
TTL加随机偏移,比如基础过期时间3600秒,实际设为3600 + random(0, 600);避免批量导入时统一写入相同EXPIRE - 兜底降级:当 Redis
PING超时或redis-cli -c连不上时,自动切换到“直连数据库 + 本地缓存(如 Caffeine)”,并限制本地缓存大小和 TTL,防止内存爆炸 - 别依赖哨兵自动切换:哨兵切换有秒级延迟,期间请求照样打穿;真正高可用得靠客户端多节点探测 + 快速失败(failfast)+ 备用数据源
最常被忽略的点是:这三个问题从来不是孤立发生的。一次雪崩可能暴露穿透漏洞(因缓存全空,大量非法请求涌入),一次击穿可能加速雪崩(因 reload 逻辑写错,把错误空值批量刷进缓存)。所以监控必须联动——比如 redis miss rate 异常时,同步看 db slow log 和 application error rate,才能准确定位是穿透还是击穿引发的连锁反应。

















