雪崩是必然发生的,关键在于发生时间与影响程度;大量key同时过期导致缓存层失效,请求瞬时涌向数据库,而redis qps数万远超mysql几千的承载能力,10万key秒级过期即压垮数据库。

雪崩不是“会不会发生”的问题,而是“什么时候发生、影响多大”的问题——只要批量设相同过期时间或单点Redis宕机,雪崩就具备触发条件。
为什么大量key同时过期会引发雪崩
缓存层失效后,请求直接打到数据库,而Redis QPS常达数万,MySQL通常只能扛住几千。10万个key在秒级内集体过期,等于把原本由Redis分担的负载瞬间全压给数据库。
常见诱因包括:活动商品预热时统一设置 EXPIRE 为3600秒;配置中心推送后批量刷新缓存并硬编码相同TTL;定时任务清缓存时未做分片。
- 不要用
redis.setex(key, 3600, value)直接写死过期时间 - 避免在上线脚本里执行
redis.keys("product:*") | xargs -n 100 redis.expire {} 3600这类批量操作 - 监控项必须包含
expired_keys指标突增(单位:秒级增幅 > 500/s)
如何给过期时间加随机扰动
核心是让失效时间“散开”,而不是“对齐”。随机范围不宜过大(否则热点数据长期不更新),也不宜过小(起不到分散效果)。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
Python示例中常用 random.randint(1, 180) 给基础TTL加1~3分钟扰动,但要注意:
- 业务允许的最大 stale 时间决定基础TTL,比如库存数据不能超过5分钟陈旧,那就设基础为300秒,扰动上限控制在60秒内
- Java里别用
new Random().nextInt(),多线程下可能生成重复种子,改用ThreadLocalRandom.current().nextInt(1, 61) - 如果用Lua脚本批量写入,
redis.call("SETEX", key, base_ttl + math.random(1,60), value)必须确认Redis版本 ≥ 3.2(math.random在早期版本不可用)
Redis实例宕机时怎么快速止损
单点故障比过期集中更危险——它让100%请求瞬间穿透,且无法靠时间分散缓解。
熔断和降级必须前置到SDK层,不能靠应用代码判断:
- 客户端连接池检测到连续3次
Connection refused或Timeout,自动切换至熔断状态,后续请求直接返回预设兜底值(如空列表、默认价格),不再尝试连接Redis - 熔断持续时间建议设为30秒,太短易抖动,太长恢复慢;恢复时采用“半开”模式:每10秒放行1%请求探活
- 别依赖哨兵自动failover——主从切换耗时通常2~8秒,这期间所有写请求失败,读请求若没配只读副本也会中断
真正难的是权衡:加随机值会增加缓存不一致窗口;熔断能保数据库,但用户看到的是“服务暂不可用”而非“数据稍旧”。这些取舍没有标准答案,得看你的业务能否接受5秒延迟、是否允许空结果展示、有没有备用缓存通道。










