Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 392|回复: 0

redis缓存存在的隐患及其解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-5 02:22:47 | 显示全部楼层 |阅读模式

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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-23 11:14 , Processed in 0.065599 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表