redis hash構造はあるkeyの下のすべての(field,values)の方法を遍歴する.

4791 ワード

本文は同時にhttps://github.com/zhangyachen/zhangyachen.github.io/issues/95
redisのhash構造には、次のようなデータが格納されています.
$input = array(
    "key" => $key,      //   key 
    "qid" => $qid,      //  id
    "value" => $startTime_$endTime,     //    _    
)

需要:毎日未明にタイミングスクリプトを走って、1つのkeyの下のすべてのqidを飛び出して、現在の時間とvalueを判断して、$endTimeどうやってすべての(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中間層が返却データに64 KBサイズの制限をしており、返却パケットのサイズが64 KBを超えるとエラーが返される.
  • 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のように一度に全部読めるものではないということである.しかし、ループループ操作は、次の2つの方法で解決できると思います.
  • ループスルー操作を一括読み出しに変更:
  • $input = array(
        'reqs' => array(
            $input1,
            $input2,
            ...,
        ),
    );
    $rpc->$cmd($input);
  • データベースにstartTimeとendTimeの2つのフィールドを追加し、どの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 endTimequery($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コマンドにより、1つのkeyの下のすべてのqidをループ読み出します.利点は明らかです.
  • HGETALLよりも、1つのキーの下にある全ての(field,value)を読み取ることができる.しかし、ループ読み出しであるため、redisサーバのリソースと時間の消費は少ない.
  • DBにkey=>qidのマッピングを冗長に格納する必要がなく、操作も比較的簡単である.

  • しかし、悲しいことに、会社のredisサービス層はhscanコマンドをサポートしていません.
    array(2) {
      ["err_no"]=>
      int(405)
      ["err_msg"]=>
      string(37) "Unknown Method: unknown method(HSCAN)"
    }

    あきらめる.
    以上、第2の方法として、DBにkey=>qidのセットを格納する.
    転載先:https://www.cnblogs.com/zhangyachen/p/8033279.html