一、缓存穿透、击穿、雪崩的区别
1. 缓存穿透
定义: 查询的数据在 Redis 和数据库中都不存在,导致每次请求都绕过缓存访问数据库。
产生原因:
- 查询不存在的数据。
- 恶意请求大量不存在的 Key。
解决方案:
- 缓存空值
- 查询数据库后,如果数据不存在,就将空值缓存到 Redis,并设置较短的过期时间。
- 优点:实现简单,能够减少重复的无效查询。
- 缺点:占用缓存空间;数据后来创建时,可能暂时读到空值。
- 布隆过滤器
- 将可能存在的数据提前加入布隆过滤器。
- 请求到来时,先判断数据是否可能存在。
- 如果确定不存在,直接返回,不再查询 Redis 和数据库。
- 优点:空间效率高,可以过滤大量无效请求。
- 缺点:存在误判,标准实现不支持直接删除元素。
2. 缓存击穿
定义: 某个热点 Key 突然失效,大量并发请求同时访问数据库,造成数据库压力骤增。
产生原因:
- 热点数据过期。
- 热点数据被删除或失效。
解决方案:
- 互斥锁
- 只有一个线程获取锁并查询数据库、重建缓存。
- 其他线程等待、重试或返回暂时不可用。
- 优点:减少重复查询,重建逻辑清晰。
- 缺点:其他请求可能等待,增加响应延迟。
- 逻辑过期
- 缓存数据中保存业务过期时间,而不是直接依赖 Redis 的物理过期时间。
- 数据逻辑过期后,仍保留在 Redis 中。
- 请求可以先返回旧数据,同时由一个线程异步重建缓存。
- 优点:请求延迟低,能够减少数据库瞬时压力。
- 缺点:允许短暂读取旧数据,需要处理重建失败等问题。
3. 缓存雪崩
定义: 大量缓存 Key 同时失效,或者 Redis 整体不可用,导致大量请求集中访问数据库。
产生原因:
- 大量 Key 设置了相同的过期时间。
- Redis 服务宕机或发生网络故障。
解决方案:
- 过期时间加随机值:避免大量 Key 同时过期。
- 热点数据不过期或使用逻辑过期:降低关键数据集中失效的风险。
- Redis 高可用:根据需求使用副本、Sentinel 或 Redis Cluster 等方案。
- 限流、熔断和降级:控制数据库承受的请求量,优先保障核心业务。
- 本地缓存:缓存部分热点数据,在 Redis 故障时提供有限的缓冲。
4. 三者对比
| 对比项 | 缓存穿透 | 缓存击穿 | 缓存雪崩 |
|---|---|---|---|
| 核心原因 | 查询的数据不存在 | 热点 Key 失效 | 大量 Key 失效或缓存整体不可用 |
| 影响范围 | 无效查询请求 | 单个热点 Key | 大量业务请求 |
| 主要方案 | 缓存空值、布隆过滤器 | 互斥锁、逻辑过期 | 随机过期时间、高可用、限流降级 |
记忆口诀:穿透是查不到,击穿是热点没了,雪崩是大量缓存一起失效。
二、布隆过滤器
1. 原理
布隆过滤器是一种空间效率较高的概率型数据结构,用于判断一个元素是否可能存在于集合中。
基本原理:
- 初始化一个所有位均为
0的位数组。 - 添加元素时,使用多个哈希函数计算数组位置,并将对应的位设置为
1。 - 查询元素时,重新计算这些位置:
- 只要有一个位置为
0,就能确定元素不存在。 - 如果所有位置都是
1,只能说明元素可能存在。
- 只要有一个位置为
2. 缺点
- 存在误判:可能把实际不存在的元素判断为存在。
- 标准实现不支持直接删除:随意将某些位改回
0,可能影响其他元素的判断。 - 无法直接获取原始数据:不能替代 Redis 或数据库。
- 需要合理设置容量和误判率:容量超出预期时,误判率可能升高。
注意:标准布隆过滤器不会漏判已经正确添加且未被错误修改的元素。
三、互斥锁与逻辑过期
1. 互斥锁
核心思想:只允许一个线程重建缓存,其他线程等待或重试。
处理流程:
- 多个请求发现热点 Key 不存在。
- 所有请求尝试获取锁。
- 只有一个线程获取成功。
- 获得锁的线程查询数据库并重建缓存。
- 其他线程等待或重试,读取重建后的缓存。
分布式场景中,可以使用 Redis 的 SET key value NX EX seconds 获取带过期时间的锁,并在释放锁时校验持有者身份。
特点:
- 优点:减少数据库重复查询,通常偏向数据新鲜度。
- 缺点:请求可能等待,增加响应延迟。
- 注意:需要考虑锁过期、异常释放和并发更新等问题。
2. 逻辑过期
核心思想:缓存数据不立即物理删除,而是由应用程序判断业务过期时间。
处理流程:
- 从 Redis 读取缓存数据及逻辑过期时间。
- 如果未过期,直接返回数据。
- 如果已过期,尝试获取锁。
- 获得锁的线程异步查询数据库并重建缓存。
- 请求可以先返回旧数据,等待缓存更新。
特点:
- 优点:请求通常不需要等待缓存重建,延迟较低。
- 缺点:可能读取旧数据,需要处理异步重建失败。
- 适用场景:允许短时间数据不一致的热点数据。
3. 两种方案对比
| 对比项 | 互斥锁 | 逻辑过期 |
|---|---|---|
| 缓存失效后 | 等待或重试 | 先返回旧数据 |
| 数据新鲜度 | 通常更偏向读取新数据 | 允许短暂不一致 |
| 请求延迟 | 可能较高 | 通常较低 |
| 核心目标 | 控制并发重建 | 保证请求可快速返回 |
记忆:互斥锁是宁愿等一等,也要尽量读新数据;逻辑过期是宁愿暂时读旧数据,也不让请求一起冲击数据库。
四、面试回答速记
1. 什么是缓存穿透?如何解决?
缓存穿透是指查询的数据在缓存和数据库中都不存在,导致请求每次都访问数据库。可以通过缓存空值或布隆过滤器解决。
2. 什么是缓存击穿?如何解决?
缓存击穿是指热点 Key 失效后,大量并发请求同时访问数据库。可以使用互斥锁控制缓存重建,或者使用逻辑过期,让请求先返回旧数据,再异步重建缓存。
3. 什么是缓存雪崩?如何解决?
缓存雪崩是指大量缓存 Key 同时失效,或者 Redis 整体不可用,导致大量请求冲击数据库。可以通过随机过期时间、Redis 高可用、限流、熔断和降级等措施解决。
4. 布隆过滤器的原理和缺点是什么?
布隆过滤器通过多个哈希函数将元素映射到位数组。查询时只要有一位为 0,就能确定元素不存在;所有位都为 1,则表示可能存在。其缺点是存在误判,标准实现不支持直接删除。
5. 互斥锁和逻辑过期有什么区别?
互斥锁控制只有一个线程重建缓存,其他请求等待或重试;逻辑过期保留旧数据,允许请求先读取旧数据,再由一个线程异步重建。前者偏向数据新鲜度,后者偏向低延迟。