加入收藏 | 设为首页 | 会员中心 | 我要投稿 阜阳站长网 (https://www.0558zz.cn/)- AI行业应用、低代码、混合云存储、数据仓库、物联网!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

PHP+Redis解决缓存击穿的实际问题

发布时间:2022-08-11 11:33:52 所属栏目:优化 来源:互联网
导读:PHP+Redis解决实际问题:缓存击穿 1、本系列文章每期都将解决一个Redis实际问题 2、每期问题将在每期的评论中选取 3、问题限Redis相关,其它问题如果本人感兴趣也不排除开辟新系列 4、本人常用PHP所以解决方案以PHP为主 5、评论里没有合适的提问时我会自己给
  PHP+Redis解决实际问题:缓存击穿
 
  1、本系列文章每期都将解决一个Redis实际问题
  2、每期问题将在每期的评论中选取
  3、问题限Redis相关,其它问题如果本人感兴趣也不排除开辟新系列
  4、本人常用PHP所以解决方案以PHP为主
  5、评论里没有合适的提问时我会自己给自己出题
 
  问题描述:
 
  本期为第二期,依旧是自己出题【狗头】
 
  PHP+Redis如何简单避免缓存击穿
 
  解决方案:
 
 
 
  <?php
 
   
 
      /**
 
       * 防击穿缓存
 
       * @param string $key       缓存key
 
       * @param int $expire       缓存过期时间(s)
 
       * @param bool $refresh     是否强制刷新数据
 
       * @param callable $func    获取要缓存的数据的回调方法(仅支持返回类型:?string)
 
       * @return null|string      返回缓存的值,没有值时且没有抢到刷新权且缓存已过期时返回null
 
       */
 
      function onceCache(string $key,int $expire,bool $refresh,callable $func): ?string
 
      {
 
          //内存,注:如果是cli模式不要使用
 
          static $dataStatic=[];
 
   
 
          //读取内存
 
          if(isset(self::$dataStatic[$key])){
 
              return self::$dataStatic[$key];
 
          }
 
   
 
          //获取laravel的获取redis连接,其它框架需要修改
 
          $redis=Redis::connection();
 
   
 
          //原缓存key加后缀"lock"为锁的key
 
          $lockKey=$key.':lock';
 
   
 
          //锁是否有效
 
          $lock=false;
 
   
 
          //读取数据
 
          $data=$redis->get($key);
 
   
 
          //如果 非强制刷新 且 缓存非空 ,获取锁
 
          if(!$refresh && !is_null($data)){
 
              $lock = $redis->get($lockKey);
 
          }
 
   
 
          if(!$lock){//如果锁过期或无数据
 
              $lock=$redis->setnx($lockKey,1);    //仅当锁不存在时设置锁,值1代表数据获取中
 
              if($lock || $refresh){              //抢到新锁 或 强制刷新
 
                  try {
 
                      $data=$func();              //从回调函数获取要缓存的数据
 
   
 
                      //有数据则写入缓存,没有则删除数据
 
                      if(!is_null($data)){
 
                          $redis->set($key,$data);
 
                      }else{
 
                          $redis->del([$key]);
 
                      }
 
   
 
                      $redis->set($lockKey,2,'ex',$expire);   //设置锁的过期时间,值2代表数据获取完成
 
                  }catch (Exception $exception){
 
                      $redis->del([$lockKey]);                //发生异常,删除锁
 
                  }
 
              }else{
 
                  //如果没有数据,又没有抢到锁
 
                  //30秒内每秒判断抢到锁的用户是否执行完成,执行完成则从缓存得到数据并返回
 
                  $retry=0;
 
                  do{
 
                      sleep(1);
 
                      $retry++;
 
                      $data = $redis->get($key);
 
                  }while(is_null($data) && $redis->get($lockKey)==1 && $retry<30);
 
              }
 
          }
 
   
 
          //写入内存
 
          if(!is_null($data)){
 
              $dataStatic[$key]=$data;
 
          }
 
   
 
          //返回数据
 
          return $data;
 
      }
 
  代码解读:
 
  数据本体永不过期
 
  设置锁的过期时间,锁过期则抢到锁的用户刷新数据
 
  未抢到锁的用户如果有拿到数据就直接返回(此时数据已经是过期数据,如果对数据及时性要求高的需要自己改造代码)
 
  未抢到锁的用户如果没有拿到数据则每秒判断抢到锁的用户是否执行完成
 
  锁有极小概率变成死锁,最好有定时任务定期处理,比如每天业务低谷期清理死锁

(编辑:阜阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读