zoukankan      html  css  js  c++  java
  • redis缓存存在的隐患及其解决方案

    redis缓存
    1.缓存穿透
    1>.什么是缓存穿透?
    业务系统需要查训的数据根本不存在,当业务系统查询时,
    首先会在缓存中查训,由于缓存中不存在,然后在往数据
    库中查,由于该数据在数据库中也不存在,数据库返回为空。

    综上所述:业务系统访问数据库中不存在的数据陈伟缓存穿透。
    2>.缓存穿透的危害:
    海量请求同一条数据库中不存在的数据,这些请求不经过缓存,
    直接访问数据库,数据库压力剧增,业务系统中属IO最为脆弱,
    这种危害可能会导致系统奔溃。
    3>.为什么会发生缓存穿透?
    (1).恶意攻击,故意制造大量不存在的数据,破坏整个系统。
    (2).代码逻辑错误。
    4>.解决方案:
    (1).缓存空的数据:
    redis以键值对存储数据,当第一请求数据时,数据不存在,将数据库返回的
    的结果为空储存在指定的健中,后续发送请求时直接相应客户端数据不存在,
    无需再次查询数据库。
    (2).缓存空数据存在两个问题:
    <!>.空值做了缓存,以为这缓存中要存更多的健,需要占用更多的内存空间,
    如果是攻击,问题会更加严重,应该给这个健设置一个过期时间,让他自动删除。
    <2>.缓存层和存储层的数据会有一段时间窗口不一致,会对业务有一定的影响
    比如设置5分钟过期,如果缓存层添加这个数据,有一段时间就会出现与数据库不一致,
    此时就利用消息系统或者其他方式清除缓存层的空对象
    (3).布隆过滤器:
    在缓存层再添加一层障碍,布隆过滤器中存储目前数据库所存在的所有key
    当业务系统请求查训时,首先在布隆过滤器中查找key是否存在,若不存在,则说明
    数据库中没有该条数据,因此缓存就不要查了,直接返回空对象给客户端,
    若存在则进入缓存中查训,如果没有再查数据库。

    这个方式是用于数据命中率不高,数据相对固定稳定时性低(通常数据集较大)
    的应用场景,代码维护复杂,但缓存占用空间较少。
    (4).两种方案比教:
    对于恶意攻击,查训的key往往不同,而且数据较多,此时,第一种方案比较合适,因为
    它存储所有空数据的Key,对恶意攻击的key往往不相同,而且每个key往往只执行一次,
    而不在使用第二次,但它保护不了数据库。
    对空数据的的key各不相同,key重复请求依据场合而言,应该选用第二种方案,对于空数据的
    key数量有限,key重复请求依据场合而言,应该选用第一种。2.缓存雪崩:

    1>.什么是缓存雪崩?
    如果缓存因某种原因发生宕机,或者存在缓存中的数据大面积的是失效,原本
    缓存抵挡的海量查训全部用涌向缓存库,因而导致整个系统崩溃。
    2>.如何避免缓存雪崩:
    (!).将缓存中的数据失效时间错开,过期时间做一个均匀分布的处理。
    (2).排斥锁:第一个线程来读取数据,缓存中没有,先访问数据库,后续线程
    再过来访问就必须等待第一个线程访问数据库成功后,再从缓存中访问。
    (3)使用分布式锁,这当然是考虑到在分布式环境下,读请求会落到集群中的不同应用服务机器上。分布式锁可以选用zookeeper或基于redis的setnx这类原子性操作来实现。
    加锁时需要用到经典的double-check lock。
    本方案虽然能够减轻DB压力,防止雪崩。但由于用到了加锁排队,吞吐率是不高的。仅适用于并发量不大的场景。
    3.缓存击穿:
    1>.什么是热点数据集中失效?
    缓存中的每一条数据到会设置失效时间,过了时效时间,该数据就会自动在缓存中删除,
    从而保证数据的一致性。
    但是,对于一些请求量极高的热点数据,一旦过了失效后海量请求最终会落到数据库上,
    从而导致数据库压力极大,系统奔溃

    如果第一个线程请求缓存时,缓存中不存在,因而去查讯数据库,就在第一个线程查训数据,而数据库尚未返回查询结果是,
    后续线程持续请求,缓存中没有数据,这些请求都会查训数据库,给数据库造成压力,
    其次,这些线程持续查询完毕后,都会重复更新缓存。
    4.缓存雪崩解决方案:
    缓存雪崩是由于原有缓存失效(过期),新缓存未到期间。所有请求都去查询数据库,而对数据库CPU和内存造成巨大压力,严重的会
    造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
      1. 碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
    public object GetProductListNew(){
      const int cacheTime = 30;
      const string cacheKey = "product_list";
      const string lockKey = cacheKey;
      var cacheValue = CacheHelper.Get(cacheKey);
      if (cacheValue != null){
        return cacheValue;
      }else{
        lock (lockKey)
      {
        cacheValue = CacheHelper.Get(cacheKey);
        if (cacheValue != null){
          return cacheValue;
        }else{

            cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。
            CacheHelper.Add(cacheKey, cacheValue, cacheTime);

          }

        }

      }
        return cacheValue;
    }
    2. 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求
    999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法。
      还有一个解决办法解决方案是:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓
    存。
    public object GetProductListNew(){
         int cacheTime = 30;
        string cacheKey = "product_list";
       //缓存标记。
       string cacheSign = cacheKey + "_sign";
       //获取缓存标记
       var sign = CacheHelper.Get(cacheSign);
       //获取缓存值
       var cacheValue = CacheHelper.Get(cacheKey);
       if (sign != null)
      {
        return cacheValue; //未过期,直接返回。
      }
     else
     {
       //缓存标记过期后重新给缓存标记随便给值,然后缓存时间为30分钟
       CacheHelper.Add(cacheSign, "1", cacheTime);
      cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。
      CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期设缓存时间的2倍,用于脏读。
      return cacheValue;
    }
    }

  • 相关阅读:
    hdu 4027 Can you answer these queries?
    hdu 4041 Eliminate Witches!
    hdu 4036 Rolling Hongshu
    pku 2828 Buy Tickets
    hdu 4016 Magic Bitwise And Operation
    pku2886 Who Gets the Most Candies?(线段树+反素数打表)
    hdu 4039 The Social Network
    hdu 4023 Game
    苹果官方指南:Cocoa框架(2)(非原创)
    cocos2d 中 CCNode and CCAction
  • 原文地址:https://www.cnblogs.com/zhanggguoqi/p/10708341.html
Copyright © 2011-2022 走看看