Redis 11 · 缓存问题:穿透、击穿与雪崩

缓存的难点不是 GET,而是缓存失效、异常流量和回源压力叠加时,系统还能不能维持服务。穿透、击穿、雪崩看起来都是"缓存没命中",根因却不同,治理手段也不能混用。

目录

  1. 先定义缓存命中链路
  2. 缓存穿透
  3. 缓存击穿
  4. 缓存雪崩
  5. 限流、降级与恢复
  6. 监控与演练

一、先定义缓存命中链路

典型 Cache Aside 链路是:查缓存 → 未命中查数据库 → 回填缓存 → 返回。任何一步超时都要有边界,不能让缓存层把数据库连接池拖垮。

二、缓存穿透

穿透是大量请求查询根本不存在的 Key,每次都绕过缓存打到数据库。攻击者可以随机生成商品 ID,普通业务流量也可能因参数校验缺失造成穿透。

第一道防线是参数校验;第二道是空值缓存,给不存在结果设置较短 TTL;第三道是布隆过滤器等存在性过滤。空值缓存要防止把后来创建的商品长期挡住,布隆过滤器的误判只能让请求继续查库,不能用于肯定存在。

三、缓存击穿

击穿是某个极热 Key 恰好过期,瞬间大量请求同时回源。互斥锁可以让一个请求重建,其余请求短暂等待;逻辑过期则允许返回旧值,同时异步刷新。

锁的等待时间、重建超时和失败兜底必须明确。不能只写 SET NX,却没有锁拥有者校验和异常释放。

四、缓存雪崩

雪崩是大量 Key 同时失效,或 Redis 集群整体不可用,造成回源洪峰。TTL 加随机抖动、分批预热、多级缓存和限流可以降低峰值,但不能替代容量评估。

五、限流、降级与恢复

当数据库连接池、线程池或下游接口到达阈值时,应优先返回静态兜底、旧缓存或明确的"稍后重试",而不是无限排队。对重建任务使用单飞(single flight)和任务队列,避免每个请求都启动刷新。

六、监控与演练

关注命中率、回源 QPS、空值比例、热点 Key、重建耗时、Redis 延迟和数据库连接池。演练单 Key 过期、批量过期、缓存集群不可用三类场景,分别验证击穿和雪崩策略。

6.1 场景:不存在的商品 ID 把数据库打满

营销活动的落地页接受 productId 参数。攻击流量不断请求随机的超大 ID,Redis 中没有缓存,MySQL 索引查询虽然单次很快,却以数万 QPS 持续消耗连接池。此时命中率下降只是表象,真正的信号是"缓存未命中率高且数据库结果为空的比例异常"。

第一步在网关和接口层校验 ID 格式、范围和签名,明显非法的请求不应进入缓存。对合法格式但不存在的 ID,缓存一个显式空对象,例如 {"found":false},TTL 可以比正常商品详情短并加入随机抖动。不要用空字符串和"缓存不存在"混淆,否则排查时无法判断是缓存 miss 还是负缓存命中。

商品后台新建了原先不存在的 ID 时,要删除这条空缓存,或者用消息事件驱动失效。布隆过滤器适合商品主数据量大且变更相对少的场景:它能快速否定"不存在",但有误判,误判时仍应让请求继续经过正常的查库和回填链路。

6.2 场景:明星商品过期,一秒钟打出几万次回源

product:detail:1001 在直播间是绝对热点。它到期的瞬间,20 000 个请求发现 miss,同时去查库并回填。只加一个互斥锁还不够:如果持锁线程的数据库查询超时,等待线程全部堆积,锁释放后又会形成第二波回源。

适合详情展示类数据的方案是逻辑过期:缓存内容带 expireAt,过期后第一个请求提交异步刷新任务,其余请求返回可接受的旧值。适合必须看到新库存的场景则用 single-flight,同一 Key 只有一个重建任务,其余请求等待一个很短的上限,超过上限返回降级页或库存繁忙。

无论哪种方案,都要限制重建任务队列。刷新失败时保留旧值并做退避,不能立即把 Key 删除后让所有请求回源。热点 Key 的 TTL 也不宜在固定整点统一失效。

6.3 场景:发布后 300 万 Key 同时失效

一次预热脚本把所有详情 Key 都设成固定一小时 TTL,整点同时过期。Redis 并没有故障,但数据库 QPS 暴涨、线程池排队,最终引发级联超时。这是典型雪崩,不是 Redis "扛不住"。

修复包括 TTL 基值加随机窗口、按批次预热、给回源接口做并发阈值与熔断、在 CDN/本地缓存放静态兜底。恢复时不要一次性清缓存或全量预热,应按热度分批回填,并用数据库连接池水位决定下一批速度。

6.4 降级不是返回错误对象那么简单

库存接口不能因为 Redis 超时就默认"库存充足",否则会把风险转成超卖。可以按业务重要性分层:商品描述返回最近一次成功缓存,推荐列表返回空列表,库存和支付直接失败并引导稍后重试。降级响应要带上来源和时间,便于客户端避免把旧数据当成实时数据。

恢复阶段先恢复 Redis 可用性,再逐步放开回源流量。设置一个"回源闸门":只有当数据库连接池、线程池和下游 P99 都低于阈值时,才提高每秒允许的重建任务数。否则缓存刚恢复,预热任务又会把数据库打垮。


记住:穿透治理"无效 Key",击穿治理"单个热点同时回源",雪崩治理"很多 Key 或整层同时失效"。

相关推荐
kiss strong2 小时前
redis服务器登录(内网环境,无法使用客户端)
数据库·redis·缓存
来让爷抱一个15 小时前
2026 语义缓存实战:把命中契约写进SPEC,MonkeyCode 云端跑通
人工智能·机器学习·缓存
hweiyu0015 小时前
Redis命令:UNLINK
redis·缓存
Shaoxi Zhang15 小时前
Redis基础——五种核心数据结构(“容器”)
redis
志尊宝20 小时前
Vue3 零基础每日笔记(009):watch 侦听器——数据一变就“做事“
vue.js·笔记·缓存
载数而行5201 天前
redis集群
redis
FfHUCisI1 天前
Go 内存分配器概览:从 TCMalloc 到三级缓存架构
缓存·架构·golang
2401_833269301 天前
RecyclerView的缓存复用机制
缓存
Escalating_xu1 天前
【Linux mmap 文件映射】从虚拟地址到页缓存:读写文件、匿名映射与极简 malloc
linux·缓存