開発プロセス中に、ユーザーのチェックイン、その日のアクティブユーザーの統計、各ユーザーのオンラインステータスなどの開発ニーズが発生する場合があります。対応するニーズに応じて、従来の方法を使用してデータベーステーブルを設計する場合があります。これは多くのストレージ容量を消費し、パフォーマンスもあまり良くありません。以下はシンプルで使いやすい方法です。
実装方法を紹介する前に、まずRedisのキーワード「ビットマップ」についてご紹介します
ビットマップとは
要素に対応する値や状態をビットで表し、キーは に対応します要素自体。 8 ビットでバイトを形成できることがわかっているため、ビットマップ自体によりストレージ スペースが大幅に節約されます。
Redis の BitMap
Redis は、バージョン 2.2.0 から setbit、getbit、bitcount などのビットマップ関連のコマンドをいくつか追加しました。これは新しいコマンドですが、setbit などのコマンドは set の単なる拡張であるため、新しいデータ型は追加されません。
setbitコマンド紹介
command SETBITキーオフセット値
Complexity O(1)
オフセットのキー値(文字列)のビット値を設定またはクリアします(0または1のみ)。
スペース占有と初めてスペースを割り当てるのに必要な時間
2010 MacBook Pro では、オフセット 2^32-1 (512MB 割り当て) の場合は ~300ms、オフセット 2^30 の場合は ~300ms かかります。 -1 (128MB 割り当て) ~ 80 ミリ秒、オフセットが 2^28-1 (32MB 割り当て) の場合は ~ 30 ミリ秒、オフセットが 2^26-1 (8MB 割り当て) の場合は 8 ミリ秒かかります。
おおよそのスペース占有計算式は次のとおりです: ($offset/8/1024/1024)MB
使用シナリオ 1: ユーザーのサインイン
多くの Web サイトがサインイン機能を提供しています (考慮されていません)ここ) データの実装が重要)、過去 1 か月のチェックイン状況を表示する必要がある場合、ビットマップを使用する場合はどうすればよいですか?暗号は一言で判明!
<?php $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;
使用シナリオ 2: アクティブ ユーザーをカウントします
時間をキャッシュキーとして使用し、ユーザー ID がオフセットされます。その日にアクティブな場合は、1 に設定します
それでは、特定の日/月/年のアクティブ ユーザーをどのように計算すればよいでしょうか (今のところ) 契約に従って、統計期間内のオンラインの 1 日だけがアクティブと呼ばれます)、次の redis コマンドを実行してください
コマンド BITOP 操作 destkey key [key...]
説明: 格納されている 1 つ以上の文字列キーを実行します。 binary bits ビット演算を行い、結果を 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;
使用场景三:用户在线状态
前段时间开发一个项目,对方给我提供了一个查询当前用户是否在线的接口。不了解对方是怎么做的,自己考虑了一下,使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户ID为offset,如果在线就设置为1,不在线就设置为0,和上面的场景一样,5000W用户只需要6MB的空间。
//批量设置在线状态 $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,然后自己计算 * 具体计算方法改天再写吧,之前写的代码找不见了。。。 */
其实BitMap可以运用的场景很多很多(当然也会受到一些限制),思维可以继续扩散~欢迎小伙伴给我留言探讨~
以上がRedis はユーザーのチェックインを実装し、アクティブ ユーザーをカウントし、ユーザーのオンライン ステータスをカウントします。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。