限流对缓存雪崩仅起“止血”作用而非根治,因其无法阻止穿透请求打库,且配置不当会阻塞缓存重建;需前置限流、合理粒度、动态阈值,并配合降级、互斥锁与分阶段策略。

缓存雪崩本身不是流量问题,而是缓存失效引发的流量失控;削峰只是辅助手段,不能替代过期时间打散、多级缓存等核心措施。
为什么限流对雪崩只有“止血”作用,不是根治
缓存雪崩的本质是大量请求在缓存层失效后,瞬间涌向数据库——此时请求量未必比平时高,但访问路径从“缓存命中”变成了“全量查库”。限流(如 RateLimiter、sentinel 或网关层 nginx limit_req)只能拦住一部分请求,但被放行的请求依然会穿透到数据库,且可能因排队等待导致响应延迟飙升。更危险的是:限流若配置在缓存层之后(比如只限数据库QPS),反而会让缓存重建逻辑被阻塞,延长雪崩窗口。
- 限流位置必须前置:应在接入层(如 API 网关)或服务入口处拦截,而不是等请求走到 DAO 层才限
- 限流粒度要合理:按用户 ID 或业务维度限比全局 QPS 限更稳妥,避免误杀正常热点请求
- 限流阈值需动态:固定阈值在大促期间容易误触发,建议结合
cache hit rate和db load指标自动升降
Redis宕机时,限流必须配合降级策略才有效
当 Redis 集群整体不可用(非单节点故障),所有缓存 key 都无法访问,此时单纯限流只是把“打爆数据库”变成“部分请求失败”,用户体验更差。真正有效的做法是:在限流同时启用降级开关,例如:
- 开启
fallback.enabled=true配置,让读请求直接返回兜底数据(如静态商品页、缓存副本) - 写请求走异步化:先写入本地队列(如
Disruptor或内存队列),待 Redis 恢复后再批量回写 - 关键路径(如下单)保留最小可用性:跳过非核心缓存校验(如优惠券库存缓存),用数据库行锁+乐观重试兜底
注意:redisTemplate.execute() 在连接超时时默认抛 RedisConnectionFailureException,若未捕获并触发降级,服务会直接 500,限流形同虚设。
削峰不等于压平流量,而是控制“重建缓存”的并发度
雪崩过程中最消耗资源的不是读请求本身,而是大量线程同时触发缓存重建(即查 DB + 写 Redis)。这时比全局限流更精准的做法是:对缓存重建行为做并发控制。
- 用
Redisson.getLock("lock:product:"+id)包裹 DB 查询逻辑,确保同一 key 只有一个线程重建 - 避免用
setIfAbsent+expire两步操作,必须用 Lua 脚本保证加锁和设置过期原子性 - 重建失败时要有退避机制:比如首次失败后
Thread.sleep(100),再失败则指数退避,防止线程池耗尽
这个环节一旦失控,会出现“缓存重建风暴”——大量线程卡在 DB 查询上,连接池打满,连健康检查都超时。
真正容易被忽略的点是:缓存雪崩的应对必须分阶段。失效刚发生时靠随机过期时间预防;失效已发生时靠互斥锁控并发;Redis 宕机时靠降级保主链路;而限流,只是穿插在各阶段之间、防止恶化加剧的“安全阀”。它从不单独起效。











