07 缓存穿透、击穿、雪崩与热点问题
缓存出问题时,先判断问题是哪一种
Section titled “缓存出问题时,先判断问题是哪一种”学习目标 · 能区分穿透、击穿、雪崩、热 Key 和大 Key,给出带代价的解决方案。
7.1 五个词,对应五种压力来源
Section titled “7.1 五个词,对应五种压力来源”| 问题 | 具体发生了什么 | 首选措施 | 代价 / 边界 |
|---|---|---|---|
| 穿透 | 不存在的商品被反复请求,缓存和 DB 都没有 | 参数检查、短 TTL 空值缓存,必要时布隆过滤器 | 新建数据需失效占位;布隆“可能存在”仍要查 |
| 击穿 | 一个热门 Key 到期,大量请求同时回源 | 合并同 Key 请求、互斥重建,或逻辑过期 | 等待增加延迟;旧值方案牺牲新鲜度 |
| 雪崩 | 大量 Key 同时过期,或 Redis 整体不可用 | TTL 抖动、容量与高可用、回源限流、降级 | TTL 抖动不能修复 Redis 整体故障 |
| 热 Key | 单个 Key 访问量极高 | 本地缓存、合并请求、按业务拆热点 | 多副本失效复杂;Cluster 不自动拆一个 Key |
| 大 Key | 一个 Key 的字节数或元素数过大 | 缩减字段、分页、分桶、分批迁移 / 删除 | 需要改模型;只缩短 Key 名帮助有限 |
7.2 布隆过滤器:把“不可能”挡在前面
Section titled “7.2 布隆过滤器:把“不可能”挡在前面”用一组哈希把已有 ID 标记到位图。查询若发现必要位置没有标记,可以判定“不存在”;所有位置都标记,只能判定“可能存在”。这是因为不同 ID 可能映射到同一位置。正确构建、同步且不做不安全删除的前提下没有假阴性,但有假阳性。新商品未同步进过滤器,会造成业务上的错误拦截。
它适合大量、稀疏的 ID 集合查询,不是每个 CRUD 项目都要上。Redis 8 包含相关概率结构能力,老版本/部署形态可能需要模块;本教程先用空值缓存解决常见情况,不要求手写布隆实现。
7.3 互斥重建:让一个请求回源
Section titled “7.3 互斥重建:让一个请求回源”锁实现见第 8 章。锁服务与缓存一起失效时,该流程也会失效,因此还需要应用层的回源并发上限。单机请求合并只能约束一个实例,十个实例仍可能各回源一次;是否需要跨实例协调,由数据库能否承受与业务代价决定。
7.4 逻辑过期:允许旧一点,保持能访问
Section titled “7.4 逻辑过期:允许旧一点,保持能访问”缓存值中存 data + freshUntil。读取发现逻辑时间过期时,先返回旧 data,仅由一个任务异步重建。后端后台线程池必须有界,避免每个请求 new 线程;物理 TTL 可更长,但仍需容量和最终清理策略。
适合活动页介绍、热门店铺资料等可接受短期旧值的场景;不适合拿旧库存批准下单。设定最大可接受陈旧时长,超过后限流回源或返回降级结果,不能永久返回错误数据。
7.5 Redis 故障时如何守住数据库
Section titled “7.5 Redis 故障时如何守住数据库”假设正常数据库能承受 800 QPS,缓存故障时入口却有 10,000 QPS。“所有请求 try/catch 后查数据库”仍会压垮它。需要结合每实例并发限制与实例总数、熔断、接口限流、请求合并、可接受的本地旧值,以及明确的繁忙响应。恢复时逐步放量或预热热点,避免空缓存再次集中回源。
降级不是都返回空列表:商品不存在、服务不可用、权限校验失败必须区分。鉴权和写入正确性有关的状态,故障时不能无条件放行。
- “布隆过滤器说存在,数据库就一定存在”:它允许假阳性。
- “加了 6 个 Redis 节点,单 Key 热点就分散了”:分片按 Key 路由,同一 Key 仍归属一个槽所在的主节点。
- “逻辑过期永远不失效”:物理 Key 仍可能因内存淘汰、重启或故障消失。
分别为“有人随机枚举商品 ID”“同一个爆款刚到期”“Redis 所有请求超时”写下第一条缓解措施及观察指标。再解释它不能解决的另两种问题。
自测与答案 · TTL 随机抖动能解决哪些问题?
主要分散大量 Key 的集中到期,降低一类雪崩概率。它不解决不存在 ID 的穿透、不保证单热点到期不击穿,也不能恢复宕机 Redis。练习:枚举 ID 看空结果与回源量,先校验及空值缓存;爆款到期看单 Key 回源并发,合并或互斥重建;整体超时先守住数据库,限流熔断并排查 Redis/网络。