缓存穿透、缓存击穿、缓存雪崩是什么?在你的项目中是如何预防和解决这些问题的?
核心前置理解
区分穿透和击穿的关键在于:
数据是否存在数据库中?
- 穿透:数据库中没有数据。
 - 击穿:数据库中有数据。
 
问题的根源是什么?
穿透:无效的请求或者恶意攻击,导致缓存层永远无法被命中。
击穿:热点数据缓存失效瞬间,大量请求请求数据库。
缓存穿透
原因/现象
查询一个缓存层和数据库中本来就没有的数据key。每次请求都会打到数据库中,导致数据库压力过大甚至崩溃。
预防/解决方案
- 布隆过滤器:最推荐,在缓存加一个前置预处理阶段。将所有的key哈希到一个足够的bitmap中,查询时先查询布隆过滤器是否存在,不存在直接返回,减少数据库查询。
 - 缓存空对象:数据库查询结果为空时,也将这个空结果缓存起来并设置一个较短的过期时。
 
缓存击穿
原因/现象
缓存中热点key突然失效,大量请求同时访问该key,导致大量请求打到数据库上。(数据库中有这个热点数据)
预防/解决方案
- 热点数据永不变过期:对热点数据key设置为永不过期,或者设置较长的过期时间,并配合后台线程定时刷新。
 - 互斥锁(Mutex Lock):当热点key失效时,只允许一个线程去数据库加载数据并写回缓存。
 
缓存雪崩
原因/现象
大量key在同一个时间过期,或者Redis服务器宕机,大量的请求访问数据库,数据库压力倍增或者崩溃。
跟缓存击穿的区别更在于key的数量不同,雪崩更注重于大量key过期。
预防/解决方案
错峰过期:为key设置不同的过期时间,避免同时过期。
高可用架构:搭建Redis集群(哨兵),避免单点故障。
熔断降级:当数据库压力过大时,服务进行熔断或者降级。
限流:对访问数据库的请求进行限流。
杂记
布隆过滤器能用在缓存击穿上吗?
不能。缓存击穿的前提是数据key曾经存在过,如果使用布隆过滤器,那么就布隆过滤器会判断可能存在这个数据,但是缓存层上这个数据key过期了,所以无法拦截请求到达数据库层。
这就涉及到布隆过滤器的原理,它最大的用途是判断一个数据肯定不存在,它无法判断数据一定存在。
互斥锁能用在缓存穿透这个场景上吗?只允许一个线程读取空值返回?
不推荐使用。互斥锁的前提是要锁定一个资源,缓存击穿之所有能用互斥锁是因为数据库中存在这个资源,所以锁的粒度可以加在存在的资源上。缓存穿透这个场景中数据库中没有这个数据,如果要创建一个外置的资源对象加锁的话,那么这个锁的粒度就有些大,会对其他的缓存过期查询数据库的请求也有影响,性能低下。