缓存击穿,缓存穿透,缓存雪崩的原因和解决方案(或者说使用缓存的过程中有没有遇到什么问题,怎么解决的)
- 缓存穿透:
是指查询一个不存在的数据,由于缓存无法命中,将去查询数据库,但是数据库也无此记录,并且出于容错考虑,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
解决方案:空结果也进行缓存,可以设置一个空对象,但它的过期时间会很短,最长不超过五分钟。 或者用布隆过滤器也可以解决,Redisson框架中有布隆过滤器。
- 缓存雪崩:
是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决方案:原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
- 缓存击穿
是指对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:如果这个key在大量请求同时进来之前正好失效,那么所有对这个key的数据查询都落到DB,我们称为缓存击穿。
解决方案:在分布式的环境下,应使用分布式锁来解决,分布式锁的实现方案有多种,比如使用Redis的setnx、使用Zookeeper的临时顺序节点等来实现
在使用缓存(如 Redis、Memcached 等)提升系统性能时,经常会遇到一些典型问题,包括 缓存击穿、缓存穿透、缓存雪崩。这些问题如果不妥善处理,可能导致数据库压力骤增,甚至服务崩溃。下面分别介绍它们的 原因、影响及解决方案。
一、缓存击穿(Cache Breakdown)
原因:
某个 热点 key(访问非常频繁的 key)在缓存中突然失效(过期),此时大量并发请求同时访问这个 key,由于缓存中没有,这些请求都会直接打到数据库,造成数据库瞬时压力过大,就像“击穿”了缓存保护层。
影响:
- 数据库压力骤增,可能引起慢查询甚至宕机。
- 影响系统整体响应速度与稳定性。
解决方案:
-
设置热点数据永不过期
对于访问特别频繁的数据(比如热门商品信息),不设置过期时间,而是通过后台异步更新缓存,确保缓存一直有效。 -
使用互斥锁(Mutex Lock)
当发现缓存未命中时,不是立即查数据库,而是先尝试获取一个分布式锁(如 Redis 的 SETNX),只有获取到锁的线程才去查数据库并重建缓存,其他线程等待或返回旧数据/默认值。适用于高并发且对数据实时性要求不是极端严格的场景。
-
逻辑过期
不设置真正的 key 过期时间,而是在缓存 value 中维护一个逻辑过期时间,当发现逻辑过期时,使用后台线程异步更新缓存,当前请求仍可返回旧数据。
二、缓存穿透(Cache Penetration)
原因:
用户请求了一个 根本不存在的数据(比如不存在的商品 ID、非法参数等),导致每次请求都 穿透缓存,直接打到数据库。如果恶意用户大量发起这样的请求,数据库就会承受巨大压力。
影响:
- 大量无效查询直接打到数据库,浪费资源。
- 可能被恶意利用进行攻击(比如大量查询不存在的用户 ID)。
解决方案:
-
缓存空对象(Null Caching)
当查询数据库也不存在该数据时,仍然将一个表示“空”的值(如 null、特殊标记)存入缓存,并设置较短的过期时间(如 30 秒 ~ 5 分钟),避免频繁查询数据库。注意:要防止缓存大量无意义空值占用空间。
-
布隆过滤器(Bloom Filter)
在缓存层之前加一个布隆过滤器,用于快速判断某个 key 是否 可能存在。如果布隆过滤器判断该 key 一定不存在,则直接返回,不再查缓存和数据库。适合用于 ID 类查询,提前拦截非法或不存在的请求。
-
接口层校验
加强参数校验,比如对 ID 范围、格式进行基础过滤,防止明显非法的请求进入系统。
三、缓存雪崩(Cache Avalanche)
原因:
大量缓存 key 在同一时间失效(比如设置了相同的过期时间),或者缓存服务宕机,导致大量请求直接打到数据库,造成数据库压力过大甚至崩溃,就像“雪崩”一样。
影响:
- 数据库瞬间流量暴增,可能引发服务不可用。
- 整个系统性能急剧下降。
解决方案:
-
设置不同的过期时间
避免给大量 key 设置相同的过期时间,可以在基础的过期时间上加上随机值(如基础过期时间 30 分钟 + 随机 0~10 分钟),让缓存错峰失效。 -
使用高可用缓存架构
比如 Redis 集群、主从 + 哨兵、Redis Cluster,避免单点故障导致整个缓存服务不可用。 -
熔断降级机制
当数据库压力过大时,可以使用熔断器(如 Hystrix、Sentinel)暂时拦截部分请求,返回默认值或错误页,保护数据库。 -
多级缓存策略
使用本地缓存(如 Caffeine、Guava Cache) + 分布式缓存(如 Redis)的多级架构,即使分布式缓存出现问题,本地缓存还能起到一定的缓冲作用。 -
缓存预热
系统启动时或低峰期提前将热点数据加载到缓存中,避免刚上线或重启后大量请求直接访问数据库。
总结对比表
| 问题类型 | 原因 | 解决方案概要 |
|---|---|---|
| 缓存击穿 | 热点 key 突然失效,大量请求直接访问数据库 | 互斥锁、永不过期+后台更新、逻辑过期 |
| 缓存穿透 | 请求了不存在的数据,绕过缓存直接查库 | 缓存空对象、布隆过滤器、参数校验 |
| 缓存雪崩 | 大量 key 同时失效 / 缓存服务宕机,导致请求全部压到数据库 | 设置随机过期时间、高可用缓存、熔断降级、多级缓存、缓存预热 |
实际使用中的经验分享(补充)
在实际项目中,我们通常会 组合使用多种策略来防护以上问题,例如:
- 对热门商品使用 永不过期 + 异步刷新 策略防止击穿;
- 所有查询先经过 布隆过滤器 或 缓存空对象 来防止穿透;
- 给所有缓存 key 设置 随机过期时间,并采用 多级缓存 + 熔断降级 来防止雪崩;
- 使用 Redis 哨兵或集群 提升缓存可用性;
- 结合 监控系统(如 Prometheus + Grafana)实时观察缓存命中率、响应时间、数据库负载,及时发现异常。
