Redis中的缓存穿透|缓存击穿|缓存雪崩
Redis是一种内存数据库,也就是说,它是一种存储在内存中的数据库. 相当于Redis是提供一种缓存服务,提供这种缓存服务的有很多种,包括Redis,MongoDB等,其中国内用的最多的最常见的可能就是Redis
既然是缓存服务,那么就可能存在缓存穿透,缓存击穿和缓存雪崩的现象存在,现在我们来分别详细描述一下这3种现象,以及对应的解决策略:
1. 缓存穿透
描述:缓存穿透指的是访问的是一个数据库和缓存中都不存在的key, 其实就是说访问数据库中压根不存在的数据. 整个访问流程是这样的=> 试图访问一个数据库和Redis缓存中都不存在的key, 首先访问Reids,显然不存在。于是就进一步去直接访问DB,也没有查询到相关数据,也没法写入缓存(如果DB中有,而缓存中没有,那么可以把DB中的写入缓存). 如果存在大批量的这种请求,就会每次"穿透"缓存直接抵达DB (此时缓存就好像 "穿透" 了一样), 引发DB的高并发查询导致宕机,这种现象就叫做 "缓存穿透"
解决方法:
1.1. 接口校验 => 我们可以想象,这种访问数据库DB和缓存中都不存在的key的情况, 在实际业务中应该很少出现. 如果突然出现大量的访问DB和缓存中都不存在的key的情况, 这种场景很有可能是遭受了非法攻击. =》 那么解决方法就来了,我们要想办法过滤到非法的攻击, 那就是在最外层做一层校验: 比如用户鉴权, 数据合法性校验等等. => 比如说一个人的年龄肯定是正整数,那么就可以对key为负数或者0的直接过滤掉
1.2 缓存空值 => 当访问缓存和DB都没有查询到值时,可以将空值/默认值写进缓存. 然后针对这个key设置较短的缓存过期时间.
1.3 布隆过滤器 => 这种是说,使用布隆过滤器存储所有可能访问的key, 当针对某一个key的查询进来时,先到这个布隆过滤器里看看有没有这个key, 如果没有,直接过滤,放弃这次查询。如果有,再去进一步查询缓存和数据库
2. 缓存击穿
描述:缓存击穿,重点就在这个"击"字,这说明一下子有大量的请求在同一个瞬间过来。确实是的,某一个热点Key, 存在redis缓存中, 但是刚好此时此刻,它刚好到过期时间,而就在它过期的这一瞬间, 同时有大量的请求打进来,这些大量请求先到缓存, 可是此时缓存又过期了,就只能去访问数据库了,也就是说这一瞬间,大量的请求直接打到数据库, 造成这一瞬间数据库请求量巨大,压力剧增,很可能直接打垮数据库 =》缓存击穿也有2种解决方案:
2.1 设置缓存中热点key的时候,不给热点key设置过期时间. 还有另外一种方式,就是给热点key还是正常的设置过期时间,不过在后台同时启动一个定时任务去定时的更新这个缓存
2.2 第二种就是加锁方式,锁的对象就是key, 这样,当大量查询同一个key的请求并发进来时,只能有一个请求获取到锁(互斥锁),然后获取到锁的线程查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库。
2. 缓存雪崩
描述:缓存雪崩, 是跟缓存击穿挂钩的。缓存击穿是有一个热点key过期了,而缓存雪崩是指: 在很短的时间内,有大量的缓存key过期,这样,这些针对这些过期的Key的请求,在很短时间内同时直接查询数据库,从而对数据库造成巨大的压力,严重情况下导致数据库直接宕机 ==> 所以说缓存击穿和缓存雪崩非常类似, 缓存击穿我们可以理解为单兵反抗,那么缓存雪崩就可以理解为集体起义. 根据这些理解,我们可以看到,缓存雪崩会出现在以下2种情况:
2.1 => 短时间内有大量缓存同时过期
2.2 => 缓存服务宕机,导致了某一个时刻发生了大规模的缓存失效
那么,针对缓存雪崩,有哪些解决方案呢:
解决方案1: 我们可以看出,既然缓存雪崩是因为在很短的时间内,大量的缓存key同时过期导致的。那么我们就想,如何避免这些大量的key同时过期呢, 那就是让这些key不同时过期, 那么如何让这些key不同时过期呢? =》 显然,可以在添加缓存key时,添加随机时间,比如0-60s, 这样每个key的过期时间都是随机的,就很少会有同时过期的情况发生
解决方案2: 热点数据不过期,这个方案和缓存击穿中的解决方案一样,也可以在后台同时启动一个定时任务去定时的更新这个缓存
解决方案3:加上互斥锁, 这种方式也和缓存击穿一样,按照key维度去加锁,对于同一个Key, 只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。