防止过期缓存被流量打穿(缓存雪崩或缓存穿透)是缓存设计中的一个关键问题。以下是一些有效的方法来避免这些问题:
1. 设置不同的过期时间(过期抖动)
-
问题:如果大量缓存同时过期,可能导致瞬时高并发请求直接打到后端服务,造成压力激增。
-
解决方案:为不同缓存键设置一个随机范围内的过期时间(如
60s + rand(0,10s)
),使缓存失效时间分散,减少同一时间的大量请求同时触发缓存更新。
2. 缓存预热
-
问题:缓存过期后,第一次请求可能会因未命中缓存导致后端压力。
-
解决方案:
-
在缓存即将过期前,后台异步任务或定时任务预先加载并更新缓存。
-
例如,使用类似「双写缓存」策略,定期将新的数据更新到缓存中。
-
利用消息队列通知更新。
3. 请求锁或请求合并(Cache Stampede)
-
问题:大量并发请求同时访问同一缓存键时,会导致所有请求同时访问后端服务。
-
解决方案:
-
请求锁:当某个键的缓存过期时,使用分布式锁(如 Redis 的
SETNX
)控制只有一个请求去加载数据并更新缓存,其他请求等待或返回旧缓存。
-
请求合并:将多个并发请求合并为一个请求,减少对后端的压力。
4. 永不过期缓存(逻辑过期)
-
问题:缓存失效可能造成瞬时流量打击后端。
-
解决方案:
-
缓存层设置为永不过期,但数据过期逻辑由应用层控制。
-
通过附加时间戳标记数据有效期,如果检测到逻辑过期,则后台异步更新缓存。
5. 限流与降级
-
问题:当缓存打穿导致后端服务承压时,可能进一步影响整体服务稳定性。
-
解决方案:
-
实施流量限流(如令牌桶算法)对突发流量进行限制。
-
实施降级策略(如返回默认值或备用数据),以减少对后端的压力。
6. 多级缓存
-
问题:单一缓存层无法有效应对高并发下的缓存击穿。
-
解决方案:
-
增加缓存层级,如本地缓存(如 Caffeine)+ 分布式缓存(如 Redis)。
-
本地缓存优先命中,减少对分布式缓存和后端的压力。
7. 布隆过滤器(防止缓存穿透)
-
问题:无效或非法请求不断击打缓存,导致所有请求直接到后端。
-
解决方案:
-
使用布隆过滤器快速判断请求是否存在合法性,拦截非法请求。
-
例如,判断缓存键是否可能存在,如果布隆过滤器判断不存在,则直接返回,避免访问后端服务。
8. 异步重建缓存
-
问题:同步重建缓存可能导致请求阻塞。
-
解决方案:
-
将缓存重建任务交由后台异步任务处理,用户请求返回旧数据或占位数据,待缓存完成更新后再提供新的数据。
结合实际业务需求,推荐综合使用多种方法,例如设置随机过期时间、引入请求锁和逻辑过期机制,以及多级缓存和布隆过滤器的配合,可以最大程度地降低缓存打穿的风险。通过监控和流量分析工具,持续优化缓存策略,也是保障系统稳定性的关键步骤。