一、缓存穿透(Cache Penetration)
1. 什么是缓存穿透?
缓存穿透是指查询一个根本不存在的数据,导致每次请求都绕过缓存,直接打到数据库上。
例如:
- 用户请求 ID 为 -1 的商品(非法或恶意请求)。
- 数据库中本就没有该数据,缓存自然也不会有。
- 每次请求都会穿透缓存,直击数据库。
2. 危害
- 数据库承受大量无效查询压力。
- 可能被恶意攻击者利用,发起大量无效请求,造成数据库崩溃。
3. 解决方案
布隆过滤器(Bloom Filter)
- 在缓存前加一层布隆过滤器,快速判断"某个 key 是否可能存在"。
- 如果布隆过滤器判断 key 不存在,则直接返回,不再查数据库。
- 注意:布隆过滤器存在"误判"(可能把不存在的判断为存在),但不会漏判。
缓存空值(Null Cache)
- 对于查询结果为空的 key,也写入缓存(如缓存
null或特殊标记),并设置较短的过期时间(如 1~5 分钟)。 - 防止同一无效 key 被反复查询。
- 注意:需防止恶意构造大量不同无效 key 导致缓存污染。
二、缓存击穿(Cache Breakdown)
1. 什么是缓存击穿?
缓存击穿是指某个热点 key 在缓存过期的瞬间,大量并发请求同时涌入,全部打到数据库上。
例如:
- 某爆款商品详情页缓存 5 分钟过期。
- 过期那一刻,10 万用户同时访问,缓存未命中,全部请求数据库。
2. 危害
- 瞬间高并发压垮数据库。
- 虽然只影响单个 key,但如果是核心热点数据,后果严重。
3. 解决方案
互斥锁(Mutex Lock)
- 当缓存失效时,只允许一个线程去数据库加载数据,其他线程等待或重试。
- Redis 中可用
SET key value NX EX实现分布式锁。 - 缺点:增加延迟,可能造成请求堆积。
逻辑过期(永不过期 + 后台更新)
- 缓存中不仅存数据,还存一个"逻辑过期时间"。
- 请求时发现逻辑过期,则触发异步任务更新缓存,当前请求仍返回旧数据。
- 保证高可用,避免数据库瞬时压力。
热点数据永不过期
- 对已知的热点 key,设置永不过期,通过后台定时任务或消息队列主动刷新。
- 需配合业务逻辑,确保数据一致性。
三、缓存雪崩(Cache Avalanche)
1. 什么是缓存雪崩?
缓存雪崩是指大量缓存 key 在同一时间失效(或 Redis 宕机),导致所有请求涌向数据库。
与击穿的区别:
- 击穿:单个热点 key 失效。
- 雪崩:大批量 key 同时失效,或整个缓存系统崩溃。
2. 危害
- 数据库瞬间承受海量请求,极可能宕机。
- 系统整体不可用,影响范围广。
3. 解决方案
设置随机过期时间
- 在基础过期时间上加一个随机值(如 300s ± 60s)。
- 避免大量 key 同时过期。
python
expire_time = 300 + random.randint(0, 120) # 5分钟 ± 2分钟
高可用架构
- Redis 集群 + 主从复制 + 哨兵(Sentinel)或 Redis Cluster。
- 避免单点故障导致整个缓存不可用。
限流降级 & 熔断机制
- 使用 Hystrix、Sentinel 等组件,在数据库压力过大时自动降级。
- 返回默认值、错误提示或排队等待。
多级缓存
- 本地缓存(如 Caffeine) + Redis 缓存。
- 即使 Redis 挂了,本地缓存还能扛一阵。
四、对比总结表
| 问题 | 触发原因 | 影响范围 | 典型场景 | 核心解决思路 |
|---|---|---|---|---|
| 缓存穿透 | 查询不存在的数据 | 单个/多个无效 key | 恶意攻击、参数错误 | 布隆过滤器、缓存空值 |
| 缓存击穿 | 热点 key 过期,高并发访问 | 单个热点 key | 秒杀、爆款商品 | 互斥锁、逻辑过期、永不过期 |
| 缓存雪崩 | 大量 key 同时过期或缓存宕机 | 大面积 key | 系统重启、批量缓存失效 | 随机过期、高可用、限流降级 |
五、最佳实践建议
- 预防为主:设计缓存策略时就考虑这三种风险。
- 监控告警:监控缓存命中率、数据库 QPS,及时发现异常。
- 压测演练:模拟高并发场景,验证系统容错能力。
- 兜底机制:即使缓存失效,也要有服务降级方案,保障核心功能可用。
结语
缓存是提升系统性能的利器,但若使用不当,反而会成为系统的"阿喀琉斯之踵"。理解缓存穿透、击穿与雪崩的本质区别,并采取针对性措施,才能构建高可用、高性能的缓存体系。
正所谓:"缓存用得好,性能没烦恼;缓存没管好,半夜被叫跑。"