缓存击穿本质是单热点key过期瞬间的并发冲击,需通过互斥锁(如set nx ex)、多级缓存协同、永不过期+异步刷新等策略防护,并重视锁超时设置、原子解锁、脏数据校验及刷新失败兜底。

缓存击穿本质是单热点Key过期瞬间的并发冲击
它不是缓存没数据,而是“刚巧在那一毫秒,所有请求同时发现缓存没了”,然后全涌向数据库。单靠加Redis机器或调大连接池没用——问题不在容量,而在时间点上的并发集中。必须在缓存层就做串行化或错峰控制。
用 SET key value NX EX 60 实现轻量级互斥锁
这是最常用也最容易出错的方案。NX 确保只有一线程能设成功,EX 60 防死锁,但要注意:
-
EX时间必须明显大于数据库查询+写缓存耗时,否则锁提前释放,其他线程又会重复回源 - 不能只靠
DEL key解锁——若业务异常退出,锁就永远卡住;应改用 Lua 脚本原子判断+删除 - 客户端拿到锁后,必须严格检查缓存是否已被别的线程写入(防止双重写),再决定是否查库
多级缓存下击穿防护要分层落地
本地缓存(如 Caffeine)和 Redis 组成 L1+L2 架构时,击穿风险其实在两级都存在:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- L1 本地缓存若未设置过期或未与 Redis 同步失效策略,可能返回脏数据;建议对热点 key 设置短 TTL + 主动刷新
- L2 Redis 的锁必须跨服务实例生效,不能只靠本地锁;
SETNX必须作用于同一个 Redis 实例(或集群模式下的同一 slot) - 如果 L1 命中但 L2 已失效,不能直接放行——需走统一锁流程,否则本地缓存反而放大击穿
永不过期 + 后台异步刷新更适合静态热点
对榜单、配置类等更新不频繁但访问极高的 key,比加锁更稳:
- 启动时预热加载,设为永不过期(
SET key value不带EX) - 另起一个定时任务(如 Spring
@Scheduled(fixedDelay = 300000)),定期查库并SET更新 - 注意:更新过程必须保证原子性,避免中间态;推荐用
GETSET或 Lua 替换整个值
真正容易被忽略的是“后台刷新失败时的兜底”——比如网络抖动导致一次刷新失败,缓存不会自动降级,系统仍会一直用旧值。得配监控告警,甚至引入版本号字段,在读取时校验 freshness。










