核心前置理解

区分穿透和击穿的关键在于:

  1. 数据是否存在数据库中?

    • 穿透:数据库中没有数据。
    • 击穿:数据库中有数据。
  2. 问题的根源是什么?

    • 穿透:无效的请求或者恶意攻击,导致缓存层永远无法被命中。

    • 击穿:热点数据缓存失效瞬间,大量请求请求数据库。


缓存穿透

原因/现象

查询一个缓存层和数据库中本来就没有的数据key。每次请求都会打到数据库中,导致数据库压力过大甚至崩溃。

预防/解决方案

  • 布隆过滤器:最推荐,在缓存加一个前置预处理阶段。将所有的key哈希到一个足够的bitmap中,查询时先查询布隆过滤器是否存在,不存在直接返回,减少数据库查询。
  • 缓存空对象:数据库查询结果为空时,也将这个空结果缓存起来并设置一个较短的过期时。

缓存击穿

原因/现象

缓存中热点key突然失效,大量请求同时访问该key,导致大量请求打到数据库上。(数据库中有这个热点数据)

预防/解决方案

  • 热点数据永不变过期:对热点数据key设置为永不过期,或者设置较长的过期时间,并配合后台线程定时刷新。
  • 互斥锁(Mutex Lock):当热点key失效时,只允许一个线程去数据库加载数据并写回缓存。

缓存雪崩

原因/现象

大量key在同一个时间过期,或者Redis服务器宕机,大量的请求访问数据库,数据库压力倍增或者崩溃。

跟缓存击穿的区别更在于key的数量不同,雪崩更注重于大量key过期。

预防/解决方案

错峰过期:为key设置不同的过期时间,避免同时过期。

高可用架构:搭建Redis集群(哨兵),避免单点故障。

熔断降级:当数据库压力过大时,服务进行熔断或者降级。

限流:对访问数据库的请求进行限流。


杂记

布隆过滤器能用在缓存击穿上吗?

不能。缓存击穿的前提是数据key曾经存在过,如果使用布隆过滤器,那么就布隆过滤器会判断可能存在这个数据,但是缓存层上这个数据key过期了,所以无法拦截请求到达数据库层。

这就涉及到布隆过滤器的原理,它最大的用途是判断一个数据肯定不存在,它无法判断数据一定存在。

互斥锁能用在缓存穿透这个场景上吗?只允许一个线程读取空值返回?

不推荐使用。互斥锁的前提是要锁定一个资源,缓存击穿之所有能用互斥锁是因为数据库中存在这个资源,所以锁的粒度可以加在存在的资源上。缓存穿透这个场景中数据库中没有这个数据,如果要创建一个外置的资源对象加锁的话,那么这个锁的粒度就有些大,会对其他的缓存过期查询数据库的请求也有影响,性能低下。