Redis - 缓存穿透、击穿、雪崩的异同点以及解决方案


缓存击穿

缓存击穿是指单个热点数据失效时,针对这个数据的大量请求会穿透到持久层,并发量高了之后,数据库宕机。【定点打击

解决方案:

1、若缓存数据基本不会发生更新,则可尝试将热点数据设置为永不过期
2、若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
3、若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。

缓存雪崩

大量的key同时失效,此时又刚好有大量的请求进来,直接打到数据库层,造成数据库阻塞甚至宕机。【批量打击

解决方案:

事前:

1)Redis 高可用,主从+哨兵,Redis cluster(集群),避免全盘崩溃。

2)原有的失效时间基础上增加一个随机值比如1-5分钟随机,不会导致同一秒内大面积的key失效

setRedis(Key,value,time + Math.random() * 10000

事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

缓存穿透

缓存穿透是指查询一个根本不存在的数据(可能由于外部的恶意攻击),缓存层和持久层都不会命中,如果一直请求攻击且并发量高,数据库宕机。

在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义。

例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,然后穿透 DB 查询依然不命中,这时会有大量请求穿透缓存访问到 DB。

解决方案:

1、 接口层增加合法性参数校验,不合法的参数直接Return,比如:id 做基础校验,id <=0的直接拦截等。

2、如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟,否则数据的实效性会产生很大的问题。
3、采用布隆过滤器拦截,将所有可能存在的数据hash到一个足够大的bitmap中。(会有一定的出错率,但不影响拦截不存在的元素)
     布隆过滤器:类似于HashSet,可以快速判断一个元素在集合中是否存在,应用场景:快速判断一个元素是否在某容器内,不存在直接返回。(关键点在于hash算法和容器大小)。其原理是利用高效的数据结构和算法快速判断出这个Key是否在数据库中存在,不存在就直接return,存在就去查了DB刷新KV再return。

总结