所属分类:php教程
使用有序集合
这种方案能够同时储存在线的用户 和 用户上线时间,能够执行非常多的聚合计算,但是所消耗的内存也是非常可观的。
使用集合
这种方案能储存在线的用户,也能够执行一定的聚合计算,相对有序集合,所消耗的内存要小些,但是随着用户量的增多,消耗内存空间也处于增加状态
使用hyperloglog
这种方案无论统计多少在线用户, 消耗的内存都是12k,但是只能给出在线用户的统计信息,无法获取准确的在线用户名单
使用bitmap
这种方案还是比较好的,在尽可能节省内存空间情况下,记录在线用户的情况,而且能做一定的聚合运算
下面我们就用实际例子来说明:
我们先以每天会有10w~30w的小量用户, 100w的用户群来说明下面的几种方案
方案一:使用有序集合
先生成用户在线记录数据:
1 2 3 4 5 6 7 8 9 10 | $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 ); } } |
好了记下来我们就来看看都能统计出哪些信息来吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //note: 统计每天的在线总人数 for ( $i =0; $i < 6; $i ++) { print_r( $redis ->zsize( '000|online_users_day_' . $i ). "\n" ); } //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))); |
不单单只有这些, 我们还能统计出早, 中, 午, 晚 等等时间段的用户在线情况,还有很多其他的,这就让我们发挥想象吧,是不是挺多的?只是确实也相当耗费内存空间
【推荐:PHP视频教程】
方案二:使用集合
还是先来成用户在线记录数据:
1 2 3 4 5 6 7 8 | //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 ); } } |
好了记下来我们就来看看都能统计出哪些信息来吧
1 2 3 4 5 6 7 8 9 10 11 12 | //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 ). "\n" ); } //note 对不同时间段的在线用户名单进行聚合 print_r( $redis ->sInterStore( '001|online_users_day_both_4and5' , '001|online_users_day_4' , '001|online_users_day_5' ). "\n" ); //note 对指定的时间段的在线用户名单进行统计 print_r( $redis ->sUnionStore( '001|online_users_day_total_4add5' , '001|online_users_day_4' , '001|online_users_day_5' ). "\n" ); //note 哪天上线哪天没上线 print_r( $redis ->sDiffStore( '001|online_users_day_diff_4jian5' , '001|online_users_day_4' , '001|online_users_day_5' ). "\n" ); |
是不是也挺不错的,先不要着急, 我们接着往下看
方案三:使用hyperloglgo
先来成用户在线记录数据:
1 2 3 4 5 6 7 8 9 | // 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 ]); } } |
这种方案,我们来看看都能实现哪些业务呢
1 2 3 4 5 6 7 8 9 | $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 ). "\n" ); } 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
笔者对这种方案其实挺喜欢的,消耗的内存空间不多, 统计的信息却挺多的,还是老步骤,先来生成数据:
1 2 3 4 5 6 7 8 | //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); } } |
接下来我们看看能满足的统计信息吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //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' )); |
怎么样?是不是集合能统计的 这家伙也能统计出来?而且消耗的内容还少。
对于这几种方案其实各有各的好处, 根据业务统计信息 来取相应的方案来实施吧,这样内存利用也就更合理了
以上就是PHP+REDIS实践:统计在线人数的几种方案分析的详细内容,更多请关注zzsucai.com其它相关文章!