zoukankan      html  css  js  c++  java
  • redis hash结构 遍历某一个key下所有的(field,values)的方法

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/95

    redis的hash结构中存储了如下的数据:

    $input = array(
        "key" => $key,      //唯一的key值
        "qid" => $qid,      //问题id
        "value" => $startTime_$endTime,     //开始时间_结束时间
    )
    

    需求:每天凌晨跑定时脚本,跑出一个key下的所有qid,判断当前时间与value,当$endTime<time()时,即为过期qid,需要删除它。

    所以我们怎么找出所有的(field,values)呢,我想到了如下的方法:

    • 通过redis的HGETALL命令。
    • 在DB中存储一份key=>qid的集合。
    • 通过redis的HSCAN命令。

    通过redis的HGETALL命令

    $input = array(
        "key" => $key,
    );
    
    $ret = $objRedis->HGETALL($input);
    var_dump($ret);
    

    result:

    array(3) {
      ["ret"]=>
      array(1) {
        ["business_AdvancedPackageOne_2"]=>
        array(16) {
          [0]=>
          array(2) {
            ["field"]=>
            string(2) "58"
            ["value"]=>
            string(3) "1_1"
          }
          [1]=>
          array(2) {
            ["field"]=>
            string(2) "56"
            ["value"]=>
            string(5) "56_57"
          }
          [2]=>
          array(2) {
            ["field"]=>
            string(2) "57"
            ["value"]=>
            string(5) "57_58"
          }
          ...
        }
      }
      ["err_no"]=>
      int(0)
      ["err_msg"]=>
      string(2) "OK"
    }
    

    但是此方法有2个缺点:

    • 公司的redis中间层对返回数据做了64KB大小的限制,如果返回包的大小超过64KB,就会返回错误。
    • HGETALL会在大数据集下表现的很低效,复杂度为O(n),n为hash表的大小。

    弃用。

    在DB中存储一份key=>qid的集合

    简答来说,就是在DB中也存储一份key=>qid的映射。首先先查找key对应的qid有哪些,在去redis中查找,所需要的操作只是HGET操作:

    $input = array(
        "key" => $key,      //唯一的key值
        "qid" => $qid,      //问题id
    )
    $ret = $objRedis->HGET($input);
    

    此方法的缺点在于:从DB中取出qid,需要循环遍历qid集合,一次次去读redis,不像HGETALL那样一次可以全部读出来。但是循环遍历操作我认为可以通过如下两个方法去解决:

    • 将循环遍历操作变为批量读取:
    $input = array(
        'reqs' => array(
            $input1,
            $input2,
            ...,
        ),
    );
    $rpc->$cmd($input);
    
    • 在数据库中增加startTime和endTime两个字段,直接在数据库层判断哪些qid过期,将过期的qid取出来,直接组装成数据,调用hdel一次性全部删除(以下是我脚本中的一段代码):
    /**
     * @desc 获取当前机构行家过期的增值包1的qid集合
     * @param $objBusinessDB
     * @param $businessId
     * @param $currentTime
     * @return array
     */
    function getMergeQid($objBusinessDB,$businessId,$currentTime){
        //过期的qid集合
        $outDateQid = array();
        $outDateId = array();
    
        //查询过期的包信息
        $sql = "select * from busines_package where businessId={$businessId} and endTime<{$currentTime} and deleted=0";
        $dbRet = $objBusinessDB->query($sql);
        if($dbRet === false){
            Bd_Log::warning("select table fail.sql[".$sql."]");
            return $outDateQid;
        }
    
        foreach($dbRet as $value){
            $extpack = mc_pack_pack2array($value['extpack']);
            //合并qid信息
            if(isset($extpack['qid']) && !empty($extpack['qid'])){
                $outDateQid = array_merge($outDateQid,$extpack['qid']);
            }
            $outDateId[] = intval($value['id']);
        }
    
        $sql = sprintf("update business_package set deleted=2 where id in (%s)",implode($outDateId));
        $dbRet = $objBusinessDB->query($sql);
        if($dbRet === false){
            Bd_Log::warning("update table fail.sql[".$sql."]");
        }
    
        return $outDateQid;
    }
    
        //获取过期的qid集合
        $outDateQid = getMergeQid($objBusinessDB,$businessId,$currentTime);
        //删除redis中的qid记录
        $redisRet = $objDaoRedis->HDEL($redisKey,$outDateQid);
        if($redisRet['err_no'] != 0){
            Bd_Log::warning("delete advancedPackage fail.businessId[$businessId] currentTime[$currentTime]");
        }
    

    为了防止过期的qid在第二天重复被读取,在发现有过期的qid集合时,将对应的deleted字段设置为2。这样的话就能防止被重复读取(where deleted=0),提高效率。

    采用。

    通过redis的HSCAN命令

    关于HSCAN命令的介绍:SCAN

    基本思想是:通过redis自身的HSCAN命令,循环读取一个key下的所有qid。优点很明显:

    • 相比于HGETALL,都能读取出一个key下所有的(field,value)。但是由于是循环读取,占用redis服务器的资源和耗时都较少。
    • 不用在DB中冗余存储一份key=>qid的映射,操作也相对于简单了许多。

    但是悲催的是,公司的redis服务层不支持hscan命令:

    array(2) {
      ["err_no"]=>
      int(405)
      ["err_msg"]=>
      string(37) "Unknown Method: unknown method(HSCAN)"
    }
    

    放弃。

    综上,采用第二种方法:在DB中存储一份key=>qid的集合。

  • 相关阅读:
    ES6——>let,箭头函数,this指向小记
    伪元素与伪类的简单介绍
    CSS font-size: 0去除内联元素空白间隙
    python学习笔记1
    一个兼容IE7IE8,H5的多功能视频播放器,H5视频播放器兼容Flash视频播放器
    【数据结构】空间使用
    Matlab计算自相关和互相关
    Matlab计算两个信号的互能量
    Matlab求信号的功率和能量
    Matlab傅氏变换
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/8033279.html
Copyright © 2011-2022 走看看