缓存击穿本身不直接导致雪崩,但与redis淘汰机制(如lru)及缺乏防护措施三者共振会放大风险;热点key被无声淘汰后引发并发回源洪峰,需通过ttl随机化、volatile策略、逻辑过期等组合手段防控。

缓存击穿本身不会直接导致雪崩;但当击穿发生时,如果大量热点 key 恰好因 LRU/LFU 淘汰或主动驱逐而集中失效,再叠加过期时间未扰动,就可能滑向雪崩边缘。关键不是“击穿引发雪崩”,而是“击穿 + 淘汰机制 + 无防护”三者共振放大了风险。
为什么 Redis 淘汰机制会让击穿变严重
Redis 的 maxmemory-policy(如 allkeys-lru 或 volatile-lru)在内存压力下会主动淘汰 key。如果一个热点 key 刚好被选中淘汰,而它又没设过期时间(或过期时间极长),那它的消失是无声的——没有 expired_keys 监控告警,也没有 TTL 到期日志,但所有请求瞬间全部 miss。这比“自然过期”更难察觉,也更易触发并发回源洪峰。
常见错误现象:
- 监控显示
evicted_keys持续上涨,但expired_keys几乎为 0 - 某个 key 的 QPS 突然从 1w+ 断崖跌到 0,紧接着 DB 查询量暴增
-
INFO memory中mem_used接近maxmemory,且maxmemory_policy是lru类策略
必须给热点 key 加双重保底:过期时间 + 驱逐防护
只靠 EXPIRE 不够,因为淘汰不看 TTL;只靠 noeviction 更不行,OOM 会直接 kill 连接。真实可行的做法是组合控制:
- 所有业务 key 必须设置
TTL,哪怕只是3600秒,避免成为 LRU 候选 - 对已知热点 key(如
cache:product:10086),用PEXPIREAT设置带随机偏移的过期时间,例如System.currentTimeMillis() + 3600000 + random(0, 300000) - 在
maxmemory-policy上,生产环境禁用allkeys-*,改用volatile-lru或volatile-lfu,确保只有带 TTL 的 key 参与淘汰 - 通过
redis-cli --stat或 Prometheus 抓取evicted_keys指标,阈值告警(比如 5 分钟内 > 100 次)
击穿场景下,逻辑过期比互斥锁更能扛住淘汰冲击
互斥锁(如 SET key value NX PX 30000)依赖 key 存在才能加锁;但如果 key 已被淘汰,SET 就变成写新 key,锁逻辑可能失效或错位。逻辑过期把过期判断下移到 value 内部,天然绕过淘汰干扰:
{
"data": {"id": 10086, "name": "iPhone 16"},
"expireAt": 1734602400000
}
读取时先查 key 是否存在,存在则解析 expireAt;若已过期,才去抢锁重建。即使 key 被淘汰,只要重建成功,新 value 就带完整逻辑过期字段——淘汰不再等于“彻底消失”。
注意点:
- 不要用
String存原始 JSON,建议用HASH或序列化后SET,避免并发解析冲突 -
expireAt必须用绝对时间戳(毫秒),不能用相对秒数,否则重建时无法对齐 - 客户端需统一处理逻辑过期响应,不能把
expireAt过期当成“数据不存在”返回 404
真正容易被忽略的是:淘汰机制和过期机制是两套独立系统,监控、告警、压测都得分开覆盖。你可能调好了 TTL 随机化,却忘了看 evicted_keys 曲线——等它和 expired_keys 同时飙高,就已经是雪崩前夜了。











