Java-缓存雪崩、穿透详解以及解决方案

  • 缓存穿透

    • 什么是缓存穿透
      • 正常情况下,我们查数据都是存在,那么我们去查询一条数据库压根不存在的数据,也就是缓存和数据库都查不到,但是每次请求都会打到数据库上,这种查询不存在的数据成为缓存穿透。
    • 穿透带来的问题
      • 如果有黑客对你的系统进行攻击,用一个不存在的ID去查询数据,会产生大量的请求到你的数据库,可能会导致你的数据库由于压力过大而宕机。
    • 解决办法
      • 缓存空值
        • 之所以会发生穿透,是因为缓存中没有存储这些空数据的key,从而导致每次查询都去数据库查,那么我们就可以将这些key设置的值设为null丢到缓存中去,并且设置过期时间,后面在查询这个key的时候就会从缓存中命中,而不用在去数据库查询了。
      • BloomFilter
        • BloomFilter类似于一个hase set 用来判断某个元素是否存在于某个集合中
          • 具体概念:
            • 这种方式在大数据场景应用较多,比如Hbase中使用它去判断数据是否在磁盘上,还有爬虫场景判断url是否已经被爬取过,这种方案可以加载第一种方案中,在缓存之前在加一层BloomFilter,在查询的时候先去BloomFilter去查询key是否存在,如果不存在就直接返回,存在再走 缓存->DB
            • Java-缓存雪崩、穿透详解以及解决方案_第1张图片
    • 如何选择
      • 针对一些恶意攻击,比如攻击时带来的大量key是不存在的,那么我们采用第一种方案就会缓存下大量不存在的key,此时这种方案就不合适了。我们可以使用第二种方案过滤这些key;针对这种key异常多的时候,请求重复率比较低的数据,我们就没必要进行缓存,使用第二种方案直接过滤掉,对于空数据的key有限的、重复率比较高的,我们则可以采取第一种方式进行缓存。
  • 缓存击穿
    • 什么是击穿
      • 缓存击穿是我们可能遇到的第二个使用缓存方案可能遇到的问题。在平常高并发的系统中,大量的请求同时查询以恶搞key时此时这个key正好失效了,就会导致大量的请求都打到数据库。这种现象我们成为击穿。
    • 会带来什么问题
      • 会造成某一时刻数据库请求过大压力剧增,甚至导致宕机。
    • 如何解决
      • 上面的现象时多个线程同事查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其它的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存,后面的线程进来发现已经有缓存了,就直接走缓存
  • 缓存雪崩
    • 什么时缓存雪崩
      • 缓存的情况是说,当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求请求数据库,结果就是数据库撑不住,挂掉。
    • 解决办法
      • 事前:使用缓存集群,保证缓存服务的高可用
        • 这种方案就是发生雪崩前对缓存集群时间高可用,如果是Redis,可以使用主从+哨兵。RedisCluster 来避免Redis全盘崩溃的情况
      • 事中:使用ehcache本地缓存+Hystrix限流+降级,避免mysql被打死的情况发生
        • 使用ehcache本地缓存的目的也是考虑在RedisCluster完全不可用的时候ehcache本地缓存能支撑一阵。使用Hystrix进行限流+降级,比如一秒来5000个请求,那么我们可以设置假设有一秒只能有2000个请求通过,那么其他剩余的3000请求就会走限流逻辑,然后调用我们自己写的组件(降级),以此来保护Mysql不会被大量的请求冲击,导致宕机。
      • 事后:开启Redis持久化机制,尽快回复缓存集群
        • 一旦重启,就能从磁盘上自动加载数据回复内存中的数据
        • 防止雪崩方案图

Java-缓存雪崩、穿透详解以及解决方案_第2张图片

  • 热点数据集中失效问题
    • 我们在设置缓存的时候,一般会给缓存设置一个失效时间,过了这个时间,缓存就会失效,对于一些热点数据来说,当缓存失效以后会存在大量的请求过来,然后打到数据库,从而可能导致数据库宕机。
    • 解决办法
      • 设置不同的失效时间
        • 为了这些热点数据集中失效,那么我们在设置缓存过期时间的时候,我们让他们失效的时间错开,比如在一个基础的时间上加上或者减去一个范围的随机数
      • 互斥锁
        • 结合上面击穿的情况,在第一个请求去查询数据库的时候对他加一个互斥锁,其余的查询请求都会被阻塞,直到锁被释放,从而保护数据库,但是也是由于他会阻塞其他的线程,此时系统的吞吐量会下降,需要结合实际业务去考虑是否要这么做。

你可能感兴趣的:(Redis)