缓存雪崩预防核心在于ttl分散与可控:set支持nx/xx精细控制,setex无法单独更新过期时间;需叠加随机偏移、两级ttl、监控expired_keys等指标。

缓存雪崩发生时,set 和 setex 的 TTL 设置差异直接影响响应与一致性
缓存雪崩本质是大量 key 同时过期,导致请求穿透到后端。此时单靠“加锁重建缓存”不够,关键在预防——而预防的核心是 TTL 的分散性与可控性。setex 一步设值+过期,看似简洁,但无法对已存在 key 做“仅更新过期时间”操作;set 配合 EX 和 NX/XX 才能实现更精细的控制。
- 用
set key value EX 300 NX可避免并发写入覆盖(仅当 key 不存在时设置),适合初始化热点数据 - 用
set key value EX 300 XX可安全刷新过期时间而不误删已有值,适合后台定时续期场景 - 绝对不要对所有 key 使用固定 TTL(如统一
EX 600),应在基础值上叠加随机偏移(如600 + random(0, 120))
使用 Redisson 或 lettuce 实现带熔断的缓存加载时,注意连接池与超时配置的连锁影响
很多团队引入分布式锁或延迟双删后,发现接口 P99 延迟飙升——问题常不在逻辑,而在客户端配置不当。例如 lettuce 默认连接池最大空闲连接为 8,若后端 DB 查询平均耗时 200ms,10 个并发就可能触发连接等待,进而拖慢整个缓存加载流程。
-
lettuce中务必调大maxIdle和maxConnections,并与后端 DB 连接池大小对齐 -
Redisson的tryLock要显式设leaseTime(如 3s),避免锁残留;同时配合waitTime(如 100ms)防止线程长时间阻塞 - 所有缓存加载逻辑必须包裹
timeout:比如用CompletableFuture.orTimeout(800, TimeUnit.MILLISECONDS)主动中断慢查询,返回 stale 数据而非卡死
“允许短暂不一致”不是一句口号,而是要通过 stale-while-revalidate 模式落地
一致性与响应时间冲突最剧烈的点,往往发生在“缓存失效瞬间”。与其让所有请求排队等新数据写入,不如让一部分请求读旧值、另一部分异步刷新——这需要业务层配合 Redis 实现两级 TTL。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 写入时存两份:
SET cache:key:value "xxx" EX 300(主缓存),再SET cache:key:stale "xxx" EX 360(延展缓存) - 读取时先查主缓存,命中则直接返回;未命中则查
cache:key:stale,若存在,立即返回并触发异步刷新(不阻塞当前请求) - 异步刷新成功后,再
DEL cache:key:stale,确保下次不再走 stale 分支
监控 expired_keys 和 evicted_keys 指标比预估流量更重要
很多雪崩事故发生在低峰期,因为运维按峰值配了内存,却没关注 key 的实际生命周期分布。Redis 的 INFO stats 中 expired_keys 突增 5 倍,往往比 QPS 上涨更早暴露问题。
- 用
redis-cli --stat快速观察每秒过期量,对比历史基线(如日常 200/s,某日凌晨突增至 15000/s,大概率有批量 key 设了相同过期时间) -
evicted_keys非零说明 maxmemory 触发了 LRU 清理,此时即使没雪崩,也已进入不稳定状态——需检查是否误存了大 Value 或未设 TTL 的 key - 不要依赖客户端记录“缓存命中率”,它掩盖了过期集中度;真正关键的是
expired_keys / total_commands_processed这个比值
真正难的不是加锁或设随机 TTL,而是把“过期行为”当作一个可测量、可调度的系统指标来看待。一旦习惯盯着 expired_keys 调整发布节奏,雪崩就从事故变成常规运营参数。










