REDIS实践之在线人数统计几种方案分析
在线人数统计业务是我们开发web肯定要设计的业务逻辑,本文就会给出几种设计方案,来分析下各个方案的优缺点:
- 使用有序集合
- 使用集合
- 使用hyperloglog
- 使用bitmap
下面我们就用实际例子来说明:
我们先以每天会有10w~30w的小量用户, 100w的用户群来说明下面的几种方案
方案一:使用有序集合
先生成用户在线记录数据:
$start_time = mktime(0, 0, 0, 9, 5); //monday for ($i=0; $i < 6; $i++) { $day_start_time = $start_time + 86400 * $i; //every day begin time $day_end_time = $day_start_time + 86400; //every day end time $online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000 for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 1000000); $redis->zadd("000|online_users_day_".$i, mt_rand($day_start_time, $day_end_time), $user_id); } }
好了记下来我们就来看看都能统计出哪些信息来吧
//note: 统计每天的在线总人数 for ($i=0; $i < 6; $i++) { print_r($redis->zsize("000|online_users_day_".$i). " "); } //note: 统计最近6天都在线的人数 var_dump($redis->zInter("000|online_users_day_both_6", [ "000|online_users_day_0", "000|online_users_day_1", "000|online_users_day_2", "000|online_users_day_3", "000|online_users_day_4", "000|online_users_day_5" ] )); //note: 统计出近6天中共有多少上线 $redis->zunion("000|online_users_day_total_6", ["000|online_users_day_0", "000|online_users_day_1", "000|online_users_day_2", "000|online_users_day_3", "000|online_users_day_4", "000|online_users_day_5"]); //note: 统计某个时间段总共在线用户 print_r($redis->zcount("000|online_users_day_5", mktime(13, 0, 0, 9, 10), mktime(14, 0, 0, 9, 10))); //note: 统计某个时间段在线用户名单 print_r($redis->zrangebyscore("000|online_users_day_5", mktime(13, 0, 0, 9, 10), mktime(14, 0, 0, 9, 10), array("withscores" => TRUE)));
不单单只有这些, 我们还能统计出早, 中, 午, 晚 等等时间段的用户在线情况,还有很多其他的,这就让我们发挥想象吧,是不是挺多的? 只是确实也相当耗费内存空间
方案二:使用集合
还是先来成用户在线记录数据:
//note set 一般聚合 for ($i=0; $i < 6; $i++) { $online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000 for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 1000000); $redis->sadd("001|online_users_day_".$i, $user_id); } }
好了记下来我们就来看看都能统计出哪些信息来吧
//note 判断某个用户是否在线 var_dump($redis->sIsMember("001|online_users_day_5", 100030)); //note 每天在线用户总量的统计 for ($i=0; $i < 6; $i++) { print_r($redis->ssize("001|online_users_day_".$i). " "); } //note 对不同时间段的在线用户名单进行聚合 print_r($redis->sInterStore("001|online_users_day_both_4and5", "001|online_users_day_4", "001|online_users_day_5"). " "); //note 对指定的时间段的在线用户名单进行统计 print_r($redis->sUnionStore("001|online_users_day_total_4add5", "001|online_users_day_4", "001|online_users_day_5"). " "); //note 哪天上线哪天没上线 print_r($redis->sDiffStore("001|online_users_day_diff_4jian5", "001|online_users_day_4", "001|online_users_day_5"). " ");
是不是也挺不错的,先不要着急, 我们接着往下看
方案三:使用hyperloglgo
先来成用户在线记录数据:
// note HyperLogLog 只需要知道在线总人数 for ($i=0; $i < 6; $i++) { $online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000 var_dump($online_user_num); for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 1000000); $redis->pfadd("002|online_users_day_".$i, [$user_id]); } }
这种方案,我们来看看都能实现哪些业务呢
$count = 0; for ($i=0; $i < 3; $i++) { $count += $redis->pfcount("002|online_users_day_".$i); print_r($redis->pfcount("002|online_users_day_".$i). " "); } var_dump($count); //note 3 days total online num var_dump($redis->pfmerge("002|online_users_day_both_3", ["002|online_users_day_0", "002|online_users_day_1", "002|online_users_day_2"])); var_dump($redis->pfcount("002|online_users_day_both_3"));
好少啊,是的, 这种方案仅仅只能统计出某个时间段在线人数的总量, 对在线用户的名单却无能为力,但是却挺节省内存的,对统计数据要求不多情况下 ,我们便可以考虑这种方案。
方案四:使用bitmap
笔者对这种方案其实挺喜欢的,消耗的内存空间不多, 统计的信息却挺多的,还是老步骤,先来生成数据:
//note bitmap 综合前面3个的优缺点 for ($i=0; $i < 6; $i++) { $online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000 for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 1000000); $redis->setbit("003|online_users_day_".$i, $user_id, 1); } }
接下来我们看看能满足的统计信息吧
//note userid today whether online var_dump($userid = mt_rand(1, 1000000)); var_dump($redis->getbit("003|online_users_day_5", $userid)); //note how many user is online var_dump($redis->bitcount("003|online_users_day_5")); //note 6 days both online var_dump($redis->bitop("AND", "003|online_users_day_both_6", "003|online_users_day_0", "003|online_users_day_1", "003|online_users_day_2", "003|online_users_day_3", "003|online_users_day_4", "003|online_users_day_5")); var_dump($redis->bitcount("003|online_users_day_both_6")); //note 6 days total online var_dump($redis->bitop("OR", "003|online_users_day_total_6", "003|online_users_day_0", "003|online_users_day_1", "003|online_users_day_2", "003|online_users_day_3", "003|online_users_day_4", "003|online_users_day_5")); var_dump($redis->bitcount("003|online_users_day_total_6")); //note 6 days only one online var_dump($redis->bitop("XOR", "003|online_users_day_only_one_6", "003|online_users_day_0", "003|online_users_day_1", "003|online_users_day_2", "003|online_users_day_3", "003|online_users_day_4", "003|online_users_day_5")); var_dump($redis->bitcount("003|online_users_day_only_one_6"));
怎么样? 是不是集合能统计的 这家伙也能统计出来? 而且消耗的内容还少。
对于这几种方案其实各有各的好处, 根据业务统计信息 来取相应的方案来实施吧,这样内存利用也就更合理了
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。
- 上一篇:没有了
- 下一篇: PHP函数源码之SESSION实现机制