1. Redis缓存雪崩redis
发生场景数据库
当Redis服务器重启或者大量缓存在同一时期失效时,此时大量的流量会所有冲击到数据库上面,数据库有可能会由于承受不住而宕机;
因此此时的缓存层出现了错误,因而全部的请求都会到达存储层,存储层的调用量会暴涨,形成存储层也完蛋了的状况;缓存
解决方案tomcat
- 均匀分布 : 咱们应该在设置失效时间时应该尽可能均匀的分布,好比失效时间是当前时间加上一个时间段的随机值,让失效的时间保持随机性,不在同一时间点失效。
- 熔断机制 : 相似于SpringCloud的熔断器,咱们能够设定阈值或监控服务,若是达到熔断阈值(QPS,服务没法响应,服务超时)时,则直接返回,再也不调用目标服务,而且还须要一个检测机制,若是目标服务已经能够正常使用,则重置阈值,恢复使用。
- 隔离机制 : 相似于Docker同样,当一个服务器上某一个tomcat出了问题后不会影响到其它的tomcat,这里咱们可使用线程池来达到隔离的目的,当线程池执行拒绝策略后则直接返回,再也不向线程池中增长任务。
- 限流/降流机制 : 其实限流就是熔断机制的一个版本,设置阈值(QPS),达到阈值以后直接返回。
- 双缓存机制 : 将数据存储到缓存中时存储俩份,一份的有效期是正常的,一份的有效期长一点.不建议用这个方案,由于比较消耗内存资源,毕竟Redis是直接存储到内存中的。
- **集群/redis高可用:**既然一台redis扛不住,那就多弄几台,这台挂掉还有其它的能够继续工做,也就是搭建的一个集群。
2. Redis缓存穿透服务器
发生场景并发
当用户想查询某条数据,发现redis数据库没有,即缓存没有命中;继续向持久层数据库查询,仍是没有,即本次查询失败;当不少次用户查询都失败,因而请求都去请求 了持久层;此时持久层的数据库就会产生很大很大压力,因而就出现了缓存穿透。ide
解决方案高并发
布隆过滤 : 咱们能够预先将数据库里面全部的key所有存到一个大的map里面,而后在过滤器中过滤掉那些不存在的key.可是须要考虑数据库的key是会更新的,此时须要考虑数据库 --> map的更新频率问题
缓存空值 : 哪怕这条数据不存在可是咱们任然将其存储到缓存中去,设置一个较短的过时时间便可,而且能够作日志记录,寻找问题缘由工具
3. Redis缓存击穿url
缓存击穿,是指一个key很是热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
好比当前的商品降价,促销;或者微博热搜不少人同时访问,那么这个Key就是一个热点,同时扛着超高并发,集中访问,缓存就被击穿。
解决方案
使用互斥锁(mutex key)
业界比较经常使用的作法,是使用mutex。简单地来讲,就是在缓存失效的时候(判断拿出来的值为空),不是当即去load db,而是先使用缓存工具的某些带成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操做返回成功时,再进行load db的操做并回设缓存;不然,就重试整个get缓存的方法。
SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,能够利用它来实现锁的效果。在redis2.6.1以前版本未实现setnx的过时时间,因此这里给出两种版本代码参考:
//2.6.1前单机版本锁 String get(String key) { String value = redis.get(key); if (value == null) { if (redis.setnx(key_mutex, "1")) { // 3 min timeout to avoid mutex holder crash redis.expire(key_mutex, 3 * 60) value = db.get(key); redis.set(key, value); redis.delete(key_mutex); } else { //其余线程休息50毫秒后重试 Thread.sleep(50); get(key); } } } 最新版本代码: public String get(key) { String value = redis.get(key); if (value == null) { //表明缓存值过时 //设置3min的超时,防止del操做失败的时候,下次缓存过时一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //表明设置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { //这个时候表明同时候的其余线程已经load db并回设到缓存了,这时候重试获取缓存值便可 sleep(50); get(key); //重试 } } else { return value; } } mem
文献参考:
https://baijiahao.baidu.com/sid=1655304940308056733&wfr=spider&for=pc
https://blog.csdn.net/zeb_perfect/article/details/54135506