redisのbitmap使用シーン

32327 ワード

BitMapって何?


ある要素に対応する値または状態をbitビットで表し、keyは対応する要素自体である.8 bitで1 Byteを構成できることを知っていますので、bitmap自体はストレージスペースを大幅に節約します.

RedisでのBitMap


Redisは2.2.0バージョンからsetbit,getbit,bitcountなどいくつかのbitmap関連コマンドを追加した.新しいコマンドですが、新しいデータ型は追加されていません.setbitなどのコマンドはset上の拡張にすぎません.

setbitコマンドの説明


命令SETBIT key offset value複雑度O(1)keyのvalue(文字列)offsetでのbit値を設定またはクリアする(0または1のみ).
空間占有、及び第1回の空間割り当てに要する時間は、1台の2010 MacBook Proでoffsetは232-1(割り当て512 MB)で〜300 ms、offsetは230-1(割り当て128 MB)で〜80 ms、offsetは228-1(割り当て32 MB)で〜30 ms、offsetは226-1(割り当て8 MB)で8 ms必要である.大体の空間占有計算式は:($offset/8/1024/1024)MB

使用シーン1:ユーザーの活躍


1週間のユーザーログイン状況
127.0.0.1:6379> setbit Monday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Monday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Monday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Tuesday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Tuesday 8298191 0
(integer) 0
127.0.0.1:6379> setbit Tuesday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Wednesday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Wednesday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Wednesday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Thursday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Thursday 8298191 0
(integer) 0
127.0.0.1:6379> setbit Thursday 8892198 0
(integer) 0
127.0.0.1:6379> setbit Friday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Friday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Friday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Saturday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Saturday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Saturday 8892198 0
(integer) 0
127.0.0.1:6379> setbit Sunday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Sunday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Sunday 8892198 0
(integer) 0


次に、7日間のログイン行動を持つユーザーを計算するには、月曜日から金曜日までの値をビットまたは演算するだけで、下位と演算子を補充できます.
      (&)
         ,       “ ”  。
    :0&0=0;  0&1=0;   1&0=0;    1&1=1;
     :     “1”,    “1”,   0
      
      (|)
         ,       “ ”  。
    :0|0=0;  0|1=1;  1|0=1;   1|1=1;
      :               1,   1。
     
     (^)
         ,       “  ”  。
    :0^0=0;  0^1=1;  1^0=1;   1^1=0;
 :         ,        “ ”(   ),      1,   0。

bitop operation rs key 1[key 2...]key 1 key 2をoperationし、結果をrsに保存operationはAND(AND)OR(または)NOT(非)XOR(イソ)であってもよい
最終計算7日以内にログインしたアクティブユーザー:127.0.0.1:6379> bitop OR result Monday Tuesday Wednesday Thursday Friday Saturday Sunday8298191 1|0|1|0|1|1|1|1=1 8892198 1|1|1|0|1|0|0=1 8987129 0|0|0|0|0|0|0|0|0|0|0|0|0=0ここで計算した結果、3つのuidが連続していると仮定すると110になりますが、実際の記憶位置は...1...1...0...つまり今週、2人のアクティブなユーザーがログインしたことがあります.

使用シーン2:アクティブなユーザーの統計


使用時間をcacheKeyとし、ユーザーIDをoffsetとし、当日アクティブになったら1に設定します.では、ある日/月/年のアクティブユーザーを計算するとします(一応、統計時間内にオンラインがある日だけアクティブと呼ばれます)、次のredisのコマンドコマンドコマンドコマンドコマンドBITOP operation destkey[key...]説明:バイナリビットを保存する文字列keyを1つ以上ビット操作し、結果をdestkeyに保存します.説明:BITOPコマンドはAND、OR、NOT、XORの4種類の操作のいずれかのパラメータをサポートする
//         

$data = array(

'2017-01-10' => array(1,2,3,4,5,6,7,8,9,10),

'2017-01-11' => array(1,2,3,4,5,6,7,8),

'2017-01-12' => array(1,2,3,4,5,6),

'2017-01-13' => array(1,2,3,4),

'2017-01-14' => array(1,2)

);



//        

foreach($data as $date=>$uids) {

$cacheKey = sprintf("stat_%s", $date);

foreach($uids as $uid) {

$redis->setBit($cacheKey, $uid, 1);

}

}



$redis->bitOp('AND', 'stat', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-12') . PHP_EOL;

//     :6

echo "     :" . $redis->bitCount('stat') . PHP_EOL;



$redis->bitOp('AND', 'stat1', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-14') . PHP_EOL;

//     :2

echo "     :" . $redis->bitCount('stat1') . PHP_EOL;



$redis->bitOp('AND', 'stat2', 'stat_2017-01-10', 'stat_2017-01-11') . PHP_EOL;

//     :8

echo "     :" . $redis->bitCount('stat2') . PHP_EOL;

現在のサイトに5000 Wユーザーがいると仮定すると、1日のデータは約5000000/8/1024/1024=6 MB

使用シーン3:ユーザーのオンライン状態


この間、現在のユーザーがオンラインであるかどうかを問い合わせるインタフェースを提供してくれたプロジェクトを開発しました.相手がどのようにしているのか分からず、自分で考えてみましたが、bitmapを使うのは省スペースで効率的な方法で、keyが1つしか必要なく、ユーザーIDがoffsetで、オンラインであれば1に設定し、オンラインでなければ0に設定し、上記のシーンと同じように5000 Wユーザーは6 MBのスペースしか必要ありません.
//        
$uids = range(1, 500000);

foreach($uids as $uid) {

$redis->setBit('online', $uid, $uid % 2);

}

//        

$uids = range(1, 500000);

$startTime = microtime(true);

foreach($uids as $uid) {

echo $redis->getBit('online', $uid) . PHP_EOL;

}

$endTime = microtime(true);

//      ,  50W        25 

echo "total:" . ($endTime - $startTime) . "s";




/**

*        ,           ,      get   value,      

*            ,          。。。

*/

使用シーン4:ユーザー署名


多くのサイトでは署名機能が提供されています(ここではデータの到着を考慮しません)、最近の1ヶ月の署名状況を示す必要があります.bitmapを使用するとどうすればいいですか?一言でコードが合わない!

$redis = new Redis();
$redis->connect('127.0.0.1');


//  uid
$uid = 1;

//   uid key
$cacheKey = sprintf("sign_%d", $uid);

//          
$startDate = '2017-01-01';

//     
$todayDate = '2017-01-21';

//  offset
$startTime = strtotime($startDate);
$todayTime = strtotime($todayDate);
$offset = floor(($todayTime - $startTime) / 86400);

echo "    {$offset} " . PHP_EOL;

//  
//              ?  365/8=45.625   ,  ,      ?
$redis->setBit($cacheKey, $offset, 1);

//      
$bitStatus = $redis->getBit($cacheKey, $offset);
echo 1 == $bitStatus ? '       ' : '      ';
echo PHP_EOL;

//       
echo $redis->bitCount($cacheKey) . PHP_EOL;

/**
*             
*     ,bitCount     start end  ,             ,     " "   
*           get   value   ,    。    value    ,              45   
*             ,  30 ,      1.31KB(      ?)
*/
//          
echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;