
| 维度 | 缓存穿透 | 缓存击穿 | 缓存雪崩 |
|---|---|---|---|
| 数据存在性(缓存) | 无 | 无 | 曾有,批量过期后缺失 |
| 数据存在性(数据库) | 无 | 有 | 有 |
| 触发关键 | 不存在的 Key(如恶意构造非法ID、攻击请求) | 热点数据 + 缓存过期/失效(如热门商品缓存到期) | 大量数据设置同一过期时间,或缓存服务宕机 |
| 请求量级影响 | 持续重复请求均落库(无缓存拦截) | 并发请求集中落库(短期瞬时高并发) | 批量请求集中落库(短期海量请求) |
| 能否回写缓存 | 不能(数据库无对应数据) | 能(数据库有热点数据,查询后可回写) | 能(数据库有对应数据,查询后可回写) |
| 问题描述 | 请求的Key在缓存和数据库中均不存在(如恶意构造不存在的用户ID、非法参数),所有请求均绕过缓存直接访问数据库,导致数据库持续承受无意义的请求压力,严重时可能引发数据库过载、响应延迟甚至宕机。 | 某一热点Key(如热门商品详情、活动入口接口)的缓存因过期/意外失效而缺失,此时大量并发请求同时访问该Key,均未命中缓存并集中涌向数据库,导致数据库短期内承受极高并发压力,可能引发瞬时过载或服务卡顿。 | 大量缓存Key因设置相同过期时间而在同一时刻批量过期,或缓存服务(如Redis)因故障宕机,导致海量请求无法命中缓存,全部集中访问数据库,造成数据库短时间内请求量暴增,可能引发数据库宕机,进而导致整个服务链路雪崩。 |
| 解决方案 | 1. 布隆过滤器:初始化时将数据库中所有存在的Key哈希到布隆过滤器,请求先经过过滤器,不存在的Key直接拦截,避免访问数据库;<br> |
缓存空值:对不存在的Key缓存空值(或默认占位值),并设置短期过期时间(如1-5分钟),避免相同请求重复落库;<br>
接口层校验:在请求入口校验参数合法性(如ID格式、数值范围),拦截恶意构造的不存在Key。 | 1. 互斥锁(分布式锁):缓存缺失时,只有一个线程能获取锁并查询数据库,查询后回写缓存,其他线程等待重试(如Redis的SETNX锁);<br>
热点Key永不过期:对核心热点Key不设置过期时间,通过业务逻辑(如数据更新时主动同步)维护缓存一致性;<br>
缓存预热:高并发场景前(如活动开始前1小时),主动将热点数据加载到缓存并设置合理过期时间,避免高峰期缓存缺失。 | 1. 过期时间随机化:给每个Key的基础过期时间增加随机偏移量(如基础过期1小时+随机0-30分钟),避免批量Key同时过期;<br>
多级缓存架构:引入“本地缓存(如Guava Cache)+ 分布式缓存(如Redis)”,即使分布式缓存雪崩,本地缓存可暂存数据,减少DB压力;<br>
缓存服务高可用:部署缓存集群(如Redis Cluster)、哨兵模式,避免单点故障,确保缓存服务稳定;<br>
熔断降级机制:通过组件(如Sentinel、Hystrix)监控数据库请求量,当超过阈值时暂时拒绝部分请求或返回默认值,防止数据库被压垮。 |
## 缓存异常
### 前置知识
- 缓存穿透:数据既不在缓存中,也不在数据库中
- 缓存击穿:数据不在缓存中;高并发场景下才会有问题
- 缓存雪崩:核心:请求直接落到了数据库上
### 基本思路
- 解决缓存穿透
- 回写特殊值
- 布隆过滤器
- 解决缓存击穿:singleflight
- 解决缓存雪崩:过期时间增加随机偏移量
- 彻底的兜底方案:限流
- 服务限流
- 数据库限流
### 亮点方案
- 异地多活
- 互为备份
- 各个业务使用自己的集群,用别人的集群作为备份
- 业务1会和集群1保持心跳
- 当发现连不上Redis之后,就可以执行容错方案
- 业务1上根据流量分类处理
- 非核心流量→直接熔断
- 核心流量→一部分熔断;一部分转发集群2(流量要保守,别把集群2搞崩)
- 业务1发现集群1恢复过来了→逐步将集群2的流量转发回来
- 使用廉价灾备
- 撑住一小段时间
- 不需要高性能,能用就行
- 节省成本,降本增效