缓存穿透、击穿、雪崩与热点 Key
缓存穿透、击穿和雪崩会把流量集中到数据库。热点 key 与大 key 还会耗尽单个 Redis 节点资源。本文负责故障识别、回源保护和恢复。
1. 面试核心
先记住这几条,再读正文- 缓存穿透。 穿透持续查询不存在的数据,负缓存和布隆过滤器能减少回源。卡 · 缓存穿透
- 缓存击穿。 击穿由热点失效触发,singleflight 合并同 key 的并发加载。
- 缓存雪崩。 雪崩涉及大量键或节点失效,TTL 抖动分散到期,回源限流保护数据库。
- 热点 Key。 单个热点 key 固定路由到一个分片,本地缓存可分担读取。
- 大 Key 删除。 Redis 4.0 起提供 UNLINK,将键摘除与值内存回收分开。卡 · 热点 key
2. 缓存穿透
无效 ID、随机 key 和已删除对象都能产生穿透。缓存与数据库均无对应数据,请求却持续回源。
- 入口先检查参数格式、租户与访问权限。
- 对确认不存在的对象缓存短期空值。
- 布隆过滤器拦截确定不在集合中的 ID。
- 对异常来源限流,按结果类型监控回源量。
负缓存需区分“不存在”和“数据库查询失败”。查询失败若被当成空值缓存,会遮住真实数据。新增对象后也要清除相应负缓存。
3. 布隆过滤器维护
布隆过滤器用多个哈希位置表示集合成员。多个成员可共享位,因此存在误判。已有成员对应的位完整保留时,查询不会漏判它。
新增数据库记录尚未加入过滤器时,过滤器会拒绝该 ID。上线时可先构建新过滤器,再补齐增量并切换。重建期间需有回源或双读策略,避免丢掉新记录。
普通布隆过滤器不支持直接清位删除。共享位被清除后,其他成员会出现漏判。需要删除能力时,可选择计数或其他支持删除的结构。
Redis 6、7 的 BF 命令来自 RedisBloom 模块。部署时需确认模块可用,不能假定核心实例自带。
4. 缓存击穿
单个热点 key 失效后,并发请求会同时加载同一数据。相同的昂贵查询因此集中到数据库。
singleflight 合并同 key 的加载,由一个请求回源。其他请求等待共享结果,并设置队列上限和等待超时。进程内 singleflight 只合并本实例请求。多实例负载还需全局回源限额或跨实例协调。
逻辑过期把业务到期时间存入值中。过期后允许返回旧值,同时启动后台刷新。它适合允许旧数据的接口。冷启动没有旧值时,仍需合并加载和失败处理。
互斥重建的租约边界见分布式锁。
5. 缓存雪崩
同批键集中到期,或缓存整体不可用,都会造成雪崩。数据库要承接原本由缓存处理的请求。
- 基础 TTL 加随机抖动,分散到期时间。
- 分批预热,限制每批回源量。
- 对数据库回源设置并发和队列上限。
- 非关键接口返回可接受的旧值或明确降级结果。
- 缓存与数据库分别进行容量测试。
TTL 抖动只分散到期时间。节点故障仍需高可用与回源保护共同处理。
6. 热点 key
单个 key 路由到同一 Redis 分片。即使命中率为 100%,也会受到节点 CPU 和带宽限制。
按业务键采样请求量,再结合分片 CPU 和网络判断热点。读热点可使用本地缓存。复制多个读副本 key 时,需确认它们分布在不同槽和节点。
共享同一 hash tag 的 key 会落到同一个槽。仅修改 tag 外的后缀,无法分散该热点。副本更新还需处理失效和版本一致性。
可合并的写入可使用分片计数。要求全局严格顺序的操作,则需要统一协调。
7. 大 key
大 key 放大内存、传输、删除和迁移成本。集合元素多时,全量读取也会长时间占用执行线程。
控制值大小和集合元素数量。遍历使用 SCAN 家族,并限制返回规模。它的游标边界见 SCAN 游标遍历。
Redis 4.0 起可用 UNLINK key 删除大 key。主线程先将键从键空间摘除,内存释放交给后台处理。释放简单对象时,实现也可直接完成。后台回收仍占 CPU 和内存带宽,应限制批量删除速度。
8. 防护链路
回源保护应有完整的资源上限。
- 校验参数和权限,拦截无效 key。
- 限制来源和业务键的请求速率。
- 用本地或 Redis 缓存承接命中。
- 合并同 key 的加载,限制等待人数。
- 用回源并发、连接池和查询超时控制数据库压力。
- 失败时返回约定的旧值、缺省结果或错误。
每层的等待时间都应服从接口总预算。恢复阶段也需控制预热和重试速度。
9. 故障演练
分别演练热点过期、大批过期和 Redis 不可用。数据库变慢与客户端连接池耗尽也应覆盖。
观察回源 QPS、加载等待和各分片 CPU、网络。再对照数据库连接等待和接口尾延迟。恢复阶段限制预热速度,防止重建流量再次压满数据库。
10. 常见问题
10.1 永久 key 与重建
永不过期可以消除该键的 TTL 到期。内存淘汰、故障和删除仍可让键消失。高频数据还需更新,因此仍要设计重建保护。
10.2 布隆过滤器的查询边界
布隆过滤器返回“确定不存在”或“可能存在”。后者仍需查询数据源。前者仅对已完整加入过滤器的集合成立。新增同步与重建切换决定它能否准确覆盖当前数据。
11 · 面试题卡片
本页题目与参考回答
11.1 缓存穿透、击穿和雪崩分别如何处理
出现公司:字节跳动
考察重点
- 三类问题的流量来源和影响范围。
- 负缓存、Bloom filter、合并加载和 TTL 抖动。
- 回源限流、降级与故障恢复。
参考回答
穿透用参数校验、负缓存和布隆过滤器减少无效回源。
击穿用 singleflight 或互斥重建合并热点加载。雪崩用 TTL 抖动分散到期,并限制总回源量。
Redis 整体故障时,还需降级和数据库容量保护。热点 key 与大 key 要按分片 CPU、网络和命令延迟诊断。恢复过程同样要限制预热速度。
11.2 布隆过滤器为何会误判,新增和删除数据时要注意什么?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:共享位、数据同步和重建。
相关内容:布隆过滤器维护。
参考回答
不同成员共享位,会让不存在的值也通过检查。
已加入的成员在位完整时不会被漏判。数据库新增但过滤器尚未同步时,会出现业务漏查。普通布隆过滤器直接清位会影响其他成员,删除需另选结构。
11.3 热点 key 和大 key 如何区分,UNLINK 能消除哪些删除成本?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:访问频率、对象体积与异步释放。
参考回答
热点 key 按访问频率判断,大 key 按大小和元素数判断。
两者可以同时存在。UNLINK 先摘除键,再异步处理较重的内存释放。它减少主线程释放大对象的停顿,后台成本仍然存在。