개발 과정에서 사용자 체크인, 당일 활성 사용자 통계, 각 사용자의 온라인 상태 등에 대한 개발 요구 사항이 발생할 수 있습니다. 해당 요구 사항에 따라 데이터베이스 테이블을 설계하는 등의 작업을 수행할 수 있습니다. 이는 저장 공간을 많이 소모하며 성능도 그다지 좋지 않습니다. 다음은 간단하고 사용하기 쉬운 방법입니다.
구현 방법을 소개하기에 앞서 Redis의 'bitmap'이라는 키워드를 먼저 소개해드리겠습니다
BitMap이란 무엇입니까
비트를 사용하여 요소에 해당하는 값이나 상태를 표현하고, 키는 다음에 해당합니다. 요소 자체. 우리는 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 할당) ~80ms, 오프셋은 2^28-1(32MB 할당)은 ~30ms, 오프셋은 2^26-1(8MB 할당)은 8ms가 소요됩니다. 0bcde3313e2ded38138583429ba02673
대략적인 공간 점유 계산 공식은 다음과 같습니다. ($offset/8/1024/1024)MB
사용 시나리오 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로 설정합니다.
그러면 특정 일/월/연도의 활성 사용자를 어떻게 계산해야 합니까? 지금은) 계약에 따르면 통계 기간 내 하루만 온라인으로 활성화된다고 합니다.) 다음 redis 명령을 주세요
명령 BITOP 작업 destkey key [key...]
설명: 저장하는 문자열 키를 하나 이상 수행합니다. 이진 비트 비트 연산을 수행하고 결과를 destkey에 저장합니다.
참고: BITOP 명령은 AND, OR, NOT 및 XOR
//日期对应的活跃用户 $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 중국어 웹사이트의 기타 관련 기사를 참조하세요!