雪崩防不住,只能“打散+兜底+熔断”三手齐抓;多级缓存(Caffeine+Redis)本身不防雪崩,因其不改变集体过期本质,若未配合随机TTL、本地降级和数据库限流,仍会批量穿透至DB。

直接结论:雪崩防不住,只能“打散+兜底+熔断”三手齐抓;单纯靠 Redis 多级缓存不能 standalone 解决雪崩,必须配合随机 TTL、本地缓存降级、数据库限流三者落地。
为什么多级缓存(Caffeine + Redis)本身不防雪崩?
多级缓存只是把“单点失效”变成“分层穿透”,它不改变缓存集体过期这个根本事实。如果 Caffeine 和 Redis 的 key 都设了固定 3600 秒 TTL,凌晨 2 点照样一起清空,请求还是会批量击穿到 DB。
- 本地缓存(如
Caffeine)只在本机生效,无法同步过期时间,不同实例可能在不同时间点集体失效 -
Redis层若未加随机偏移,集群内所有节点对同一类 key 的过期时间完全一致 - 多级缓存的写入顺序(先写 Redis 再写本地)若没做原子性保障,可能造成本地有值、Redis 为空的“假命中”状态,反而掩盖问题
怎么给 Redis key 设置带随机因子的 TTL?
核心是让同一批业务 key 的过期时间呈离散分布,避免整点“集体下班”。不要用固定值,也不要只加一个常量偏移。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 推荐公式:
base_ttl + random.randint(0, base_ttl // 10)(比如基础 3600 秒,加 0~360 秒随机) - 避免使用
time.time() + ttl这种写法——如果服务重启或时钟回拨,会导致 key 提前/延后过期 - Spring Boot 中用
RedisTemplate时,别直接调expire(),应在set()时一并传入TimeUnit和随机后的秒数 - 注意:布隆过滤器、互斥锁等机制不解决雪崩,它们针对的是穿透和击穿,别混用
本地缓存(Caffeine)怎么兜底才真正有效?
本地缓存不是“加一层就完事”,它必须能独立承担 Redis 宕机期间的读请求,否则等于没兜底。
- 初始化阶段就要预热:大促前通过脚本把热点商品 ID、类目树等写入
Caffeine,而不是等第一次查询才加载 - 设置
maximumSize和expireAfterWrite,但后者 TTL 应比 Redis 的长 2~3 倍(例如 Redis 是 3600s,Caffeine 设 7200s),防止本地也跟着一起过期 - 禁用
refreshAfterWrite,它会在后台异步刷新,但雪崩发生时你更需要“稳住不动”,而不是触发新查库 - 监控
Caffeine的hitRate和evictionCount,如果淘汰率突增,说明 size 不够或数据倾斜严重
Redis 宕机时,多级缓存如何不连环崩溃?
这是最容易被忽略的一环:当 Redis 不可用,你的代码是否还在拼命重试、堆积连接、拖垮线程池?
- 必须对
RedisTemplate或Lettuce客户端配置超时:timeout=500ms,commandTimeout=300ms,避免阻塞 - 捕获
RedisConnectionFailureException后,立即 fallback 到本地缓存,且不再尝试写入 Redis(防止恢复时雪上加霜) - 数据库查完后,只写本地缓存,跳过 Redis 写入逻辑——等 Redis 恢复后再由定时任务或事件驱动补全
- 不要在 catch 块里打全量日志,高频失败时日志 IO 会压垮磁盘
真正的难点不在“怎么加缓存”,而在“缓存失效后系统是否还知道怎么活”。随机 TTL 控制节奏,本地缓存提供容错纵深,熔断和限流守住数据库底线——这三者缺一不可,且必须在压测中验证组合效果。

















