缓存击穿的本质是单点热点失效,需通过互斥锁或逻辑过期解决,多级缓存仅辅助降压;本地缓存需二次检查+主动刷新,redis 应用逻辑过期而非物理过期,三端一致性依赖锁、过期策略与主动 reload 协同。

缓存击穿的本质是单点热点失效,多级缓存不能直接防止它
多级缓存(比如本地缓存 + Redis)本身不解决缓存击穿问题。它只是把“打穿”的位置从数据库前移到了 Redis 前——如果本地缓存也为空、Redis 又刚好过期,请求照样会涌向下游。真正起作用的是「互斥锁」或「逻辑过期」,多级缓存只是辅助手段,用来降低锁竞争频率和缓解 DB 压力。
本地缓存加锁前必须做二次检查
本地缓存(如 Caffeine、Guava Cache)设了 5 分钟 TTL,但若只靠它防击穿,高并发下仍可能多个线程同时发现本地缓存 miss,然后一起去 Redis 查,再一起发现 Redis 也没值,最后全去查 DB。
- 正确做法:本地缓存 miss 后,先尝试获取分布式锁(
setIfAbsent),拿到锁才继续;没拿到锁就 sleep + 重试或直接走本地缓存兜底 - 拿到锁后,必须再次检查本地缓存和 Redis 缓存(防止其他线程已写入),确认都为空才查 DB
- 查完 DB 后,按顺序写入 Redis(带过期时间)、再写入本地缓存(可设更长 TTL 或无 TTL,靠主动刷新)
Redis 层必须用逻辑过期替代物理过期
物理过期(EXPIRE)会导致 key 瞬间消失,触发击穿;逻辑过期是把过期时间存在 value 内部,key 永不过期,读取时判断时间戳是否超限。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
- 写缓存时:存
{"data": {...}, "expireAt": 1720848000000},不设 TTL - 读缓存时:反序列化后检查
expireAt,若已过期,尝试用setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS)加锁重建 - 未过期则直接返回;过期但抢锁失败,就返回旧数据(允许短暂 stale),不阻塞请求
本地缓存的 reload 策略容易被忽略
本地缓存不是“设了就完事”。如果只靠被动失效(TTL 到期),在击穿窗口期仍可能大量线程穿透出去。需要配合主动刷新:
- 在写 DB 成功后,主动调用
localCache.put(key, newValue),而不是等自然过期 - 对高频热点 key,可启动后台线程定期预热(比如每 2 分钟 reload 一次),避免集中失效
- 本地缓存 size 要设上限(如
maximumSize(10000)),否则 OOM 风险比 Redis 还高
多级缓存真正难的不是配置层级,而是各级之间的一致性边界和失效协同。一个 key 在本地缓存、Redis、DB 三处状态不同步时,逻辑过期 + 分布式锁 + 主动 reload 才构成完整防线。漏掉任意一环,都可能让击穿在某个环节复现。










