缓存雪崩

定义

大量缓存数据在同一时间过期(失效)或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃。

应对

大量数据同时过期

  1. 避免将大量的数据设置成同一个过期时间,可以给这些数据的过期时间加上一个随机数。
  2. 加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里);实现互斥锁的时候,最好设置超时时间
  3. 对缓存数据可以使用两个 key,一个是主 key,会设置过期时间,一个是备 key,不会设置过期。key值不一致、value值一致,实质上给缓存数据做一个副本。当访问不到主key时,直接返回备key的缓存数据;更新缓存时同时更新主key和备key的数据。
  4. 让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新。不设置有效期,业务线程不再负责更新缓存。

Redis 故障宕机

  1. 启动服务熔断机制,暂停业务应用对缓存服务的访问,直接返回错误,不用再继续访问数据库,等到 Redis 恢复正常后,再允许业务应用访问缓存服务。
  2. 启用请求限流机制,只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。
  3. 通过主从节点的方式构建 Redis 缓存高可靠集群,如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务。

缓存击穿

定义

在业务中被频繁访问的数据被称为热点数据,如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。可以认为缓存击穿是缓存雪崩的一个子集。

应对

  1. 互斥锁方案,保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
  2. 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间。

缓存穿透

定义

当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。出现该情况有可能是业务误操作,也有可能是黑客恶意攻击

应对

  1. 在 API 入口处加入非法请求的限制,如果判断出是恶意请求就直接返回错误。
  2. 针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值返回,而不会继续查询数据库。
  3. 使用布隆过滤器快速判断数据是否存在。

当一个元素加入布隆过滤器中的时候,会进行如下操作:

  1. 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
  2. 根据得到的哈希值,在位数组中把对应下标的值置为 1。

当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:

  1. 对给定元素再次进行相同的哈希计算;
  2. 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在任意一个值不为 1,说明该元素不在布隆过滤器中。

由于存在哈希冲突,因此查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中就一定不存在这个数据

Reference