两种雪崩
缓存雪崩有两种形态。第一种:大批 key 同时到期——凌晨批量预热的热点数据统一设了 24 小时 TTL,第二天凌晨同一秒集体失效,或者在某一秒集中过了逻辑过期,读流量整体砸向 DB。第二种:Redis 实例整体宕机——缓存层直接消失,所有读请求回源,DB 瞬间接住平时十倍的流量。
它和限流系列第 1 篇的服务雪崩是近亲:那次讲的是慢下游拖死线程的级联传染,这次是缓存层集体失效后的流量错位——两者经常连环爆发,缓存雪崩触发服务雪崩。防线也从同一个思想出发:别让风险同时发生,别让流量无处可去。
预防针:TTL 打散
对付「同时到期」,办法朴素而有效:给 TTL 加随机抖动,让到期时间摊开在一个区间里,任何时刻只有一小部分 key 过期:
int base = 1800; // 基础 30 分钟
int jitter = ThreadLocalRandom.current().nextInt(0, 600); // 0~10 分钟随机
redis.setex(key, base + jitter, toJson(detail)); // 30~40 分钟内摊开批量预热的代码同样要打散——循环里给每个 key 独立的随机 TTL,否则预热脚本本身就成了雪崩的调度器。抖动幅度取 TTL 的一到三成,太小没效果,太大缓存水位抖动剧烈。
缓冲垫与止血带
Redis 整体挂掉时,TTL 打散无济于事——根本没缓存可打。此时的防线换一套:本地缓存挡在最前面,进程内的 Caffeine 里还留着的极热数据继续服务,分掉一部分流量;限流降级守住 DB 的门口——限流系列第 8 篇的多级防线在此复用:只放核心读流量到 DB,非核心接口直接降级返回缓存快照或兜底页;快速恢复靠 Redis 高可用——哨兵自动切换、集群副本顶上,让缓存层尽快回来,这部分到主从与哨兵篇展开。
层层设防,各就各位
把防线按时间排个序:预防——TTL 打散与预热,让雪崩没有发生条件;缓冲——本地缓存与客户端超时,让第一波冲击变软;止血——限流与降级,保住 DB 和核心链路;根治——主从哨兵与集群,让缓存层自身不再单点。四层各管一段,缺哪层哪层出事。
穿透、击穿、雪崩三只拦路虎到这里都过了招,但它们都还是「能用性」的问题。缓存真正的深水区在数据正确:库里的数据变了,缓存什么时候变、以什么顺序变——下一篇开始翻一致性这座大山。
评论 (0)