当 Redis 缓存发生了雪崩,击穿或者穿透等问题,就会造成大量的请求积压到后端数据库层,使得数据库面对大量高并发的请求发生宕机故障的问题,使得应用层不可用的问题。
一、缓存雪崩
造成雪崩的情况就是由于 redis 缓存不能处理的请求大量的发送到数据库端,使得数据库端需要承受大量高并发的请求,数据库的压力剧增。
(1)造成缓存雪崩的两种情况
- 缓存中大量的 key 在相同的时间过期
- 由于缓存中大量的 key 的过期的时间设置到相同的实际,使得缓存中需要访问大量的数据都失效了,最终导致缓存的命中率极具降低,使得大量的命令请求发送到数据库端。
- 由于缓存中大量的 key 的过期的时间设置到相同的实际,使得缓存中需要访问大量的数据都失效了,最终导致缓存的命中率极具降低,使得大量的命令请求发送到数据库端。
- redis 实例发生了宕机等故障
- 当作为缓存的 Redis 实例发生了宕机的情况,就使得 Redis 不能提供服务,使得应用层大量的数据请求发送到数据库端,使得数据库端的压力剧增。使得数据库因为无法承受使得数据库崩溃。
(2)两种发生缓存雪崩的解决方案
- 第一种由于大量键值对在同一时间过期导致发生缓存雪崩
- 我们可以使用 EXPIRE 在设置键值对过期间时间的时候在末尾小范围内增加一点随机数,避免大量的键值对在同一时刻发生过期的情况,同时也可以让这些数据能在相近的时间内过期,仍然能满足业务需求。
- 我们可以使用服务降级的方式除了一些访问核心数据的请求可以发送到数据库端以外,对于一些无关紧要数据的访问请求就不选择发送到数据库端,而是选择返回一些预定义信息,空值或者缺省值。这样就可以减缓对数据库的压力。
- 第二种由于 Redis 实例宕机等故障导致发生缓存雪崩
- 我们可以使用服务熔断机制,在 redis 实例还未恢复之前将发送过来的请求不直接发送到 redis 缓存中而是直接返回,直至 redis 实例重新恢复服务之后,才会允许请求发送到 redis 实例上。
- 上面使用服务熔断机制虽然可以有效的方式数据库端发生接收大量的请求导致崩溃的情况,但是却也因此使得暂停了这个数据的访问,对应用层的影响巨大,为了减少这样的影响,我们还可以采取使用服务限流的方式来解决。在业务系统的请求入口前端控制每秒发送请求数量控制,避免一次性大量的请求发送到数据库端。
- 以上的解决方案都属于“事后诸葛亮”的做法,我们还可以在发生缓存雪崩前做个预防的措施,就是构建一个 Redis 缓存的高可用集群,这样一旦 Redis 缓存主库发生了宕机,就可以进行主从切换,让其他的从节点却换成主节点来负责缓存服务,就可以避免由于 Redis 实例宕机造成缓存雪崩的。
二、缓存击穿
相对于缓存雪崩,缓存击穿失效的数据量比较小,发生缓存击穿的原因都是由于某些热点数据在缓存中不能提供服务,不得已大量并发操作该数据的请求发送到数据库端,使得造成数据库的压力剧增,影响了数据库处理其他的请求。
(1)造成缓存击穿的原因
- 由于缓存中的热点数据的过期时间到了,使得缓存热点数据被删除,之后大量的并发访问操作热点数据的请求一次性发送给数据库端。
(2)缓存击穿的解决方案
- 对于缓存中热点数据不选择设置过期时间,这样就可以让该数据始终保存在缓存中。
三、缓存穿透
缓存穿透就是指发送的请求中的数据即不在缓存中也不在数据库中,因此在大量的请求去访问这些不存在数据时就会由于缓存缺失,将请求发送给数据库端,又由于数据库端也不存在该数据,使得就不能将数据库中的数据读取出来写入到缓存中,如果此时有大量对这些不存在的数据进行访问就会由于缓存中持续没有对应数据的缓存,让这些请求大量的发送到数据库端,增加数据库端的压力。
(1)造成缓存穿透的原因
- 业务层失误,误删了数据库中的数据
- 恶意攻击,专门发送大量不存在数据的访问请求
(2)解决缓存穿透的解决方案
- 缓存空值或者缺省值
- 当发生了缓存穿透的情况,就在缓存中保存数据的一个空值或者是和业务层约定好的一个缺省值,之后有对这些数据访问的请求发送到缓存中就会返回一个空值或者是缺省值,避免了这些请求发送到数据库端。
- 使用布隆过滤器快速判断数据是否存在
- 使用布隆过滤器快速判断访问的数据是否存在,如果不存在就不会将该请求发送给数据库端,这样即使有大量并发的访问不存在数据的请求也只是在布隆过滤器中进行查询,并不会将这些请求发送到数据库端,并且布隆过滤器在 Redis 内存实现,而 Redis 本身就能够支撑大量的高并发访问的能力,因此不会对其造成太大的影响。
- 布隆过滤器的工作原理
- 在布隆过滤器中存在一个初始值为 0 的 bit 数组,和 N 个哈希函数组成,将数据库中存在的数据完成标记,之后在请求中要访问某个数据的时候通过计算判断是否存在数据库来决定是否发送给数据库端。布隆过滤器中的标识数据的过程就是让写入数据库中的数据经过 N 个哈希函数计算得到 N 个值,然后对 bit 数组的长度进行取余操作,使得这些计算出来的结果对应到 bit 数组中各个位置,然后将其对应的位置进行赋值为 1,就完成了数据标识的过程。当有请求发送到布隆过滤器上,就会以同样的过程,将该数据经过 N 个哈希函数计算的值对 bit 数组的长度进行取余操作,然后去查看这些对应的 bit 数组中的位置是否都赋值为 1,一旦有一个没有被赋值为 1 都会判断得出数据库中不存在该数据。因此就不会将该请求发送给数据库端,就避免了数据穿透的可能。
- 在业务层请求入口前端拦截这些恶意的请求,对发送的请求进行合理性检查,例如检查请求中的请求参数是否合理,是否合法,请求字段是否存在等。