缓存击穿是单个key过期瞬间遭遇高并发查询,表现为数据库同一SQL密集执行、Redis某key miss_rate短时超95%、线程池阻塞;需通过标记过期时间+分布式锁+动态锁时长+Lua安全释放+失败重试监控实现精准识别与防护。
如何识别正在发生的缓存击穿
缓存击穿不是“偶尔查不到”,而是某个
在过期瞬间遭遇高并发请求,大量线程同时发现缓存为空,又几乎同时去查库。典型现象包括:数据库慢查询日志里突然出现同一
的密集执行、Redis 监控中某
的
短时飙升至 95%+、应用线程池活跃数暴涨且大量线程阻塞在数据库连接上。
关键判断点是:是否集中在单个
(比如
),而不是全量缓存失效。如果是全量,大概率是缓存雪崩,得换思路。
用 Redis 命令 + 应用层埋点做
热点
Key 实时识别
别依赖事后告警——要在击穿发生前或刚冒头时捕获。核心是两件事:统计访问频次 + 判断是否临近过期。
对高风险业务 key(如商品、订单、用户主页)在写缓存时,统一加一个标记字段,例如
,存入逻辑过期时间戳
在读缓存逻辑中,先
主 key,再
对应的
,如果当前时间距该时间戳不足 30 秒,就主动触发
不推荐用
扫全量 key 做统计,会阻塞 Redis;改用
配合客户端采样,或直接开启 Redis 的
和
抓异常调用模式
自动加锁不能只靠 setIfAbsent
是基础,但生产环境必须补三处:
Redis 8.2.3
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
下载
锁 key 必须带业务前缀和唯一标识,比如
,避免不同服务误删彼此的锁
锁的过期时间不能硬写 10 秒,要根据对应数据库查询 + 缓存写入的 P99 耗时动态算,建议设为
秒
释放锁必须用 Lua 脚本校验 value 是否匹配,否则可能 A 线程锁超时被自动释放后,B 线程还没执行完就误删了 A 写的缓存 —— 这类脚本必须原子执行:
监控与降级必须联动,不能只盯 Redis
光看 Redis 的
没用。真正要监控的是“尝试加锁失败后退避重试”的次数,以及“返回逻辑过期数据”的比例。
当
的失败重试次数在 1 分钟内超过 50 次,或逻辑过期数据返回占比 > 30%,就得自动触发降级:比如把该
的缓存策略临时切为永不过期,或直接返回兜底静态页。这些动作不能靠人盯,得用 Prometheus + Alertmanager + 自研 Operator 自动执行。
最容易被忽略的是:逻辑过期方案下,后台异步刷新线程失败时,没有任何 fallback。你得确保这个线程有独立的健康检查和失败重投机制,否则“永远不过期”就真成“永远不更新”了。
keySQLkeymiss_ratekey"product:10086""product:10086:expire_ts"GETGET:expire_tstryLock("lock:product:10086")KEYSSCANslowloglatency monitorsetIfAbsent"lock:hot:product:10086"max(10, db_p99 * 2)EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:hot:product:10086 abc123hit_ratelock:hot:product:10086key