深浅主题
Redis 缓存穿透与击穿
问题现象
不存在的 ID 反复打到数据库,或热点 key 到期瞬间数据库请求暴增。前者是穿透,后者通常称击穿。
适用环境
Redis 6+,cache-aside,热点读取;示例为思路而非完整分布式锁实现。
本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。
复现或诊断步骤
- 统计缓存命中、未命中、空结果、回源并发与 DB QPS,并按 key 热度聚合。
- 检查大量不同无效 ID 还是同一个热点 ID 到期。
- 验证 TTL 是否集中到期,Redis 故障与业务真实未命中应分开统计。
原因分析
不存在的记录没有缓存导致每次回源;热点值到期时多个线程同时重建。所有 key 同一时间失效会形成雪崩。直接在 Redis 故障时无限回源可能把缓存故障扩大为数据库故障。
修复示例
text
读取 cache:item:{id}
命中普通值 -> 返回
命中显式空值标记 -> 返回不存在
未命中 -> 尝试同 key 的有限等待合并
获得重建权 -> 二次读取缓存 -> 查询数据库
有结果:写入正常 TTL + 随机抖动
无结果:写入短 TTL 的空值标记
未获得重建权 -> 有界等待后重查,或按业务降级
超过等待预算 -> 返回可识别错误,避免无限回源先校验 ID 格式和权限,再考虑短时空值缓存、请求合并或布隆过滤器。创建新记录后主动清除对应空标记。跨进程重建锁需唯一 token、过期时间与原子校验释放,业务超时超过租约时还需续期或防旧写设计。
验证方法
模拟无效 ID、热点同时过期、Redis 超时和数据库失败,观察 DB 并发是否仍受限。确认缓存故障不会被错误写成“记录不存在”,并验证新建记录可以立即读取。
常见误区
空值缓存过长会隐藏刚创建的数据。普通 DEL 释放分布式锁可能删除别人的新锁;布隆过滤器有误判,也需要与新增数据同步。