缓存穿透,缓存击穿,缓存雪崩以及解决方案

项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步

1.缓存穿透

一个缓存系统,正常情况下我们去查询的时候大部分key都是存在的。
如果去请求一个缓存系统中没有的数据,此时系统如果没有经过优化,会将请求继续打到数据库上,但其实数据库上也没有这条数据。上面这种情况就叫缓存穿透。
如果有人要对系统进行攻击,拿大量不存在的key去发起请求,你的系统将会产生大量的请求直接访问数据库,这样数据库的压力太大很有可能直接宕机,整个服务直接挂掉。

针对上面这种情况,一般有如下的解决办法。
如果对应的key空数据量级不大,且重复访问请求比较多,那么我们可以将这么key对应的value全部设为null加入缓存中。这样该key只有第一次请求时候访问数据库,后面再出现这个key的请求时候直接返回null,不会再去访问数据库。
如果异常的key很多,且没有什么重复请求,这个时候如果我们将这些key都缓存起来,将会浪费大量的内存。这个时候,可以在缓存之前先加一层BloomFilter,查询的时候先去BloomFilter中判断key是否存在。如果key不存在可以直接返回。如果key存在再去查缓存或者查后端数据库。

2.缓存击穿

对于高并发系统,如果有大量的请求同时去查询一个key,刚好这个key失效,如果没有优化大量的请求也会直接打到后端数据库,有可能导致宕机,这个叫缓存击穿。

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

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;      
          }
 }

3.缓存雪崩

当缓存系统某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到DB上面。结果就是DB hold不住,宕机了,这个就叫缓存雪崩。

针对这种情况,首先我们需要保证的是缓存服务的高可用性。比如现在redis一般是集群模式,不会是点个服务器。另外,关键服务部分可能还会使用主从+哨兵备份集群,避免redis服务全部挂掉。

使用 ehcache 本地缓存的目的也是考虑在 Redis Cluster 完全不可用的时候,ehcache 本地缓存还能够支撑一阵。使用 Hystrix进行限流 & 降级 ,比如一秒来了5000个请求,我们可以设置假设只能有一秒 2000个请求能通过这个组件,那么其他剩余的 3000 请求就会走限流逻辑。然后去调用我们自己开发的降级组件(降级),比如设置的一些默认值呀之类的。以此来保护最后的 MySQL 不会被大量的请求给打死。

另外实际中还有一个简单的方案就是使缓存失效的时间分开。比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

4.RDB与AOF

RDB是redis database。其工作原理就是在指定时间间隔内将内存中的数据集快照写入磁盘,相当于是全量备份,所以有点在于数据集小,数据备份,迁移都比较方便,而且恢复起来速度也快。但是缺点也显而易见,如果快照的时间间隔比较大,那数据丢失也比较多。因为是全量备份,当数据量较大的时候,耗时也会比较长。
AOF是append only file, 是把每一次数据变化都写入到一个aof里面。以日志的形式记录每个操作,将Redis执行过的所有指令全部记录下来(读操作不记录),只许追加文件但不可以修改文件,Redis启动时会读取AOF配置文件重构数据。换句话说,就是Redis重启就会根据日志内容从头到尾执行一次来完成数据的恢复工作。AOF的有点是数据丢失比较少而且文件可读,缺点是文件增长比较快,因为记录的是redis的操作日志,同时恢复起来速度也会比较慢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值