PHP实现统计在线人数的多种方法:表统计、Redis有序集合、Hyperloglog详解

这篇文章给大家带来了 PHP 的相关知识,主要介绍了实现统计在线人数的方法,包括利用表统计方式、用 redis 有序集合统计以及用 hyperloglog 做统计等。下面一起来看看吧,希望能对大家有所帮助。

PHP实现统计在线人数的多种方法:表统计、Redis有序集合、Hyperloglog详解

一个业务系统网站每天的访问量是多少呢?每天的在线人数又是多少呢?这种业务在我们的开发过程中需要预留,它也在我们的设计范围之内哦!因为对于一个正在运营的网站来说,每天都要进行统计。

在线人数是如何进行统计的呢?这里存在着几种方案,并且代码采用的是 laravel 框架,这些方案可以在开发过程中作为参考。

1 用表统计方式

用数据表统计在线人数,这种方式只能用在并发量不大的情况下。

首先我们先新建表:user_login

编辑

user_login 表

模拟用户登录,不存在用户就存入表,存在的则更新登录信息

// 客户端唯一的识别码
$client_id 等于 session 函数获取到的 id 。
//用户是否已存在
DB 用于操作名为“user_login”的表,其中的数据被赋值给变量 $user 。
在其中的('token',$client_id)处
    ->first();
//不存在则插入数据
if (empty($user)) {
    $data = [
“token”被映射为$client_id。
使用'username'来模拟用户,将其转换为'user_'加上$client_id
模拟用户 id 为通过 mt_rand(10000000, 99999999) 生成的数字。
'create_time' 被赋予了 'Y-m-d H:i:s' 格式的日期时间值;
'update_time' 被转换为 'Y-m-d H:i:s' 格式的日期时间
    ];
使用“DB::table('user_login')”这个表进行插入操作,插入的数据是$data。
} else {    
    // 存在则更新用户登录信息
DB 对 'user_login' 这个表进行操作。
在 'token' 这个位置,有 $client_id 。
     ->update([
'update_time' 被更新为 'Y-m-d H:i:s' 格式的日期时间
      ]);
}

这里需要定期清理没有任何操作的用户。如果用户在一个小时内没有任何操作,那么我们可以将其记为无效用户。

代码如下:

// 客户端唯一的识别码
$client_id = session()->getId();
//用户是否已存在
$user = DB::table('user_login')
    ->where('token', $client_id)
    ->first();
//不存在则插入数据
if (empty($user)) {
    $data = [
        'token' => $client_id,
        'username' => 'user_' . $client_id, // 模拟用户
        'uid' => mt_rand(10000000, 99999999),   //模拟用户id
        'create_time' => date('Y-m-d H:i:s'),
        'update_time' => date('Y-m-d H:i:s')
    ];
    DB::table('user_login')->insert($data);
} else {    
    // 存在则更新用户登录信息
    DB::table('user_login')
     ->where('token', $client_id)
     ->update([
          'update_time' => date('Y-m-d H:i:s')
      ]);
}

我们可以实现的功能:

1)当前在线人数

2)某时间段内在线人数

3)最新上线的用户

4)指定用户是否在线

// 可实现功能一:当前总共在线人数
通过 DB 操作,对表名为 'user_login' 的表进行计数操作,得到的结果赋值给变量 c,即 c 等于对 'user_login' 表进行计数后的数量。
当前在线人数为:$c 。';
// 可实现功能二:某时间段内在线人数
$begin_date 的值为 '2020-08-13 09:00:00';
$end_date 的值为 '2020-08-13 18:00:00';
$c 等于 DB 操作的表名为 'user_login' 的数据;$c 是从名为 'user_login' 的表中获取的数据;$c 是对 'user_login' 这个表进行操作后得到的结果;$c 是与 'user_login' 表相关的数据;$c 是关于 'user_login' 表的内容。
where 的 'create_time' 大于等于 $begin_date
在 'create_time' 这个条件下,其值为<=', $end_date)
    ->count();
输出开始日期 $begin_date 与结束日期 $end_date 之间的在线人数信息,即在线人数为 $c ,格式为开始日期 - 结束日期 在线人数:$c';
// 可实现功能三:最新上线的用户
$newest 等于对表 'user_login' 进行操作的结果,即通过 DB::table 方法获取到的相关数据。
按照“create_time”字段进行排序,排序顺序为“DESC”。
    ->limit(10)
    ->get();
echo '最新上线的用户有:';
foreach ($newest as $value) {
输出 $value 的 username 属性值并添加一个空格;输出一个空格;继续后续操作。
}
echo '';
// 可实现功能四:指定用户是否在线
$username 的值为 'user_1111'。
$online 等于从名为“user_login”的表中获取数据。
where 的条件为 'username' 且等于 $username
    ->exists();
输出用户名以及判断用户是否在线,如果在线则输出“在线”,如果不在线则输出“不在线”。

2 使用 redis 有序集合实现在线人数统计

内存中使得效率很高,能够统计某时间段内的在线人数,也可以进行各种聚合操作。然而,当在线人数较多时,会占用较多内存。另外,还有:

无法通过用户操作时间来清除无效用户。只有手动登出的用户才会从集合中被删除。

代码如下:

// 客户端唯一的识别码
$client_id = session()->getId();
echo $client_id . '';
// 按日期生成key
$day = date('Ymd');
$key 等于 'online:' 与 $day 进行连接;$key 是由 'online:' 和 $day 组合而成的;$key 为 'online:' 加上 $day 所形成的。
// 是否在线
Redis 利用 zScore 方法获取键 $key 中客户端 ID 为 $client_id 的值并赋值给 $is_online 。
如果($is_online 为空),那么(不在线,加入当前客户端)。
Redis 执行了将当前时间和客户端 ID 添加到指定键的集合中的操作,即 Redis::zAdd($key, time(), $client_id)。
}
// 可实现功能一:当前总共在线人数
$c = Redis::zCard($key);
echo '当前在线人数:' . $c . '';
// 可实现功能二:某时间段内在线人数
$begin_date = '2020-08-13 09:00:00';
$end_date = '2020-08-13 18:00:00';
使用 Redis 的 zCount 方法,传入键名 $key 以及开始时间 strtotime($begin_date) 和结束时间 strtotime($end_date),将返回的结果赋值给变量 $c 。
echo $begin_date . '-' . $end_date . '在线人数:' . $c . '';
// 可实现功能三:最新上线的用户,时间从小到大排序
$newest 等于 Redis 通过 zRangeByScore 方法获取的结果,获取的键为 $key,分数范围是从负无穷('-inf')到正无穷('+inf'),并且限制返回的数量为从索引 0 开始的 50 个。
echo '最新上线的用户有:';
foreach ($newest as $value) {
输出拼接后的结果。
}
echo '';
// 可实现功能四:指定用户是否在线
$username = $client_id;
$online$的值为$Redis$的$zScore$方法获取的结果,该方法的参数为$key$和$client_id$。
输出用户名以及该用户是否在线的状态信息,若在线则显示“在线”,若不在线则显示“不在线”,最后以特定字符结尾。';
// 可实现功能五:昨天和今天都上线的客户
昨天等于 Carbon 的昨天方法调用后再转换为日期字符串。
$yes_key 等于将字符串 $yestoday 中的 '-' 替换为空字符串。
$members = [];
Redis 进行管道操作,在函数内部使用 $pipe ,同时使用 $key 、$yes_key 以及引用 $members 。具体如下:
Redis 使用 zinterstore 方法创建了一个新的键 'new_key',它是通过对键 $key 和 $yes_key 进行操作得到的,并且设置了聚合方式为'min'。
$members 是通过 Redis::zRangeByScore 方法获取的,参数为 'new_key',分数范围是 '-inf' 到 '+inf',并且设置了限制条件 'limit' 为 [0, 50]。
    //dump($members);
});
echo '昨天和今天都上线的用户有:';
对于每一个 $members 中的 $value ,进行如下操作 :
    echo $value . ' ';
}

3 使用 hyperloglog 做统计

hyperloglog 与有序集合方式不同,它十分节约空间。不过,它实现的功能较为单一,仅仅能够统计在线人数,无法实现其他任何功能。

Redis HyperLogLog 是用于进行基数统计的算法。当输入元素的数量或者体积非常大时,HyperLogLog 的优点在于,计算基数所需的空间始终是固定的,而且这个空间很小。

Redis 中,每个 HyperLogLog 键仅需 12 KB 内存,便能计算接近 2^64 个不同元素的基数。而在计算基数时,元素越多耗费内存就越多的集合与之形成鲜明对比。

但是,HyperLogLog 仅会依据输入元素来计算基数,不会储存输入元素自身。因此,HyperLogLog 无法像集合那样,将输入的各个元素返回。

HyperLogLog 只需知晓在线的总人数。
for ($i=0; $i < 6; $i++) {
    $online_user_num = mt_rand(10000000, 99999999);     //模拟在线人数
    var_dump($online_user_num);
    for ($j=1; $j < $online_user_num; $j++) { 
        $user_id = mt_rand(1, 100000000);
        $redis->向 '002|online_users_day_' 加上 $i 这个键,并将 $user_id 作为值添加到这个键对应的集合中。
    }
}
$count = 0;
for ($i=0; $i < 3; $i++) { 
    $count += $redis->调用 pfcount 函数,传入参数为 '002|online_users_day_'.$i ;
输出 $redis->pfcount('002|online_users_day_'.$i) 的结果并换行
}
var_dump($count);
总共 3 天的在线数量
var_dump 的结果为合并后的结果。
使用 var_dump 函数来输出 Redis 中名为 '002|online_users_day_both_3' 的键的基数计数。

这种方案只能统计出某个时间段在线人数的总量,无法获取在线用户的名单,不过它挺节省内存的。在对统计数据要求不多的情况下,我们可以考虑采用这种方案。

4 使用 bitmap 统计

bitmap 是通过一个 bit 位来表示某个元素对应的价值或者状况的。其中的 key 就是对应的元素自身。我们知晓 8 个 bit 能够组成一个 Byte。所以 bitmap 自身能够极大地节省存储空间。

bitmap 常被用于统计在线用户。

代码如下

// 模拟当前用户
uid 等于通过 request 获取到的 'uid' ;uid 是由 request 所获取的 'uid' ;request 获取到的 'uid' 赋值给了 uid ;uid 是通过 request 获得的 'uid' ;获取到的 'uid' 通过 request 被赋值给了 uid 。
$key 为 'online_bitmap_' 与 date('Ymd') 进行连接后的内容;
// 设置当前用户在线
Redis::setBit($key, $uid, 1);
// 可实现功能1:在线人数
$c = Redis::bitCount($key);
输出“在线人数:”以及变量$c的值。';
// 可实现功能2:指定用户是否在线
通过 Redis 获取指定键 $key 中用户标识 $uid 对应的位值,将其赋值给变量 $online 。
输出 $uid ,如果在线则显示“在线”,如果不在线则显示“不在线” ,后面还有其他内容(此处未给出完整信息)。';
// 可实现功能3:昨天和今天均上线的用户总数
$yestoday = Carbon::yesterday()->toDateString();
$yes_key = str_replace('-', '', $yestoday);
$c = 0;
Redis 使用 pipeline 函数,在函数内部使用 $pipe 作为参数,同时使用 $key、$yes_key 和引用变量 $c。具体的操作是在 pipeline 中进行相关的处理。
Redis 执行位操作“AND”,目标键为“yest”,操作的键为$key 和$yes_key 。
使用 Redis 的 bitCount 方法来计算 'yest' 的比特位数量,将结果赋值给变量 c 。
});
昨天上线的用户数量有:$c ,今天上线的用户数量也有:$c 。昨天和今天都上线的用户数量为:$c 。';

bitmap 消耗的内存空间相对较少,而统计的信息却较为丰富,这种方案是值得被推荐的。

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

53阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

44阅读

php一句话木马,一句话木马ctf

2026年10月05日

48阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

50阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

47阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

42阅读