本文目录导读:

- 方案一:基于数据库的“最后活动时间”(最常用、可靠)
- 方案二:基于 Redis 的有序集合(Sorted Set)(高性能、推荐)
- 方案三:基于文件(适用于小项目或测试)
- 方案四:利用 WebSocket 或 Server-Sent Events (SSE)(实时性最强)
- 总结建议
在PHP项目中实现用户在线统计,通常有几种常用的方法,每种方法在准确性、性能和资源消耗上各有取舍,以下是几种主流的实现方案,从简单到复杂排序:
基于数据库的“最后活动时间”(最常用、可靠)
这是最经典且易于实现的方法,核心思想是在用户表中记录用户最后一次请求的时间,然后统计最近一段时间(如 5 分钟或 15 分钟)内有过请求的用户数量。
实现步骤:
-
数据库设计:在
users表中增加一个字段last_activity(DATETIME 或 TIMESTAMP)。 -
更新逻辑:在用户每次访问需要统计的页面(或者通过一个单独的 Ajax 心跳请求)时,更新该用户在当前会话中的
last_activity时间。// 假设用户已登录,用户ID存储在 $_SESSION['user_id'] session_start(); if (isset($_SESSION['user_id'])) { $userId = $_SESSION['user_id']; // 更新最后活动时间(通常只更新到秒,避免频繁写入毫秒) $stmt = $pdo->prepare("UPDATE users SET last_activity = NOW() WHERE id = ?"); $stmt->execute([$userId]); } -
统计逻辑:查询
last_activity时间大于当前时间减去 N 分钟的用户总数。// 统计过去5分钟内的在线用户 $onlineThreshold = date('Y-m-d H:i:s', strtotime('-5 minutes')); $stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE last_activity > ?"); $stmt->execute([$onlineThreshold]); $onlineCount = $stmt->fetchColumn(); echo "当前在线用户: " . $onlineCount;
优点:实现简单,数据持久化,可以准确知道是谁在线。 缺点:每次请求都更新数据库,高并发下对数据库压力较大,可考虑使用 Redis 等缓存来减轻压力。
基于 Redis 的有序集合(Sorted Set)(高性能、推荐)
使用 Redis 的有序集合(ZSET)非常适合做在线统计,用户 ID 作为 member,最后活动时间的时间戳作为 score。
实现步骤:
-
准备工作:确保 PHP 环境中安装了 Redis 扩展。
-
更新逻辑:在用户每次请求时,向一个名为
online_users的有序集合中添加或更新成员。$redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 用户ID,使用时间戳作为分数 $userId = $_SESSION['user_id']; $score = time(); // 当前时间戳 // 添加或更新 $redis->zAdd('online_users', $score, $userId); -
清理与统计:
- 清理过期用户:使用 ZREMRANGEBYSCORE 移除所有分数小于当前时间减去 N 分钟的成员。
- 统计在线人数:使用 ZCARD 获取集合大小。
// 清理5分钟前的数据 $expireTime = time() - 300; // 300秒 = 5分钟 $redis->zRemRangeByScore('online_users', 0, $expireTime);
// 获取当前在线人数 $onlineCount = $redis->zCard('online_users');
echo "当前在线用户: " . $onlineCount;
优点:高性能,内存操作,支持高并发;原子操作,数据准确。 缺点:需要额外部署和维护 Redis 服务。
基于文件(适用于小项目或测试)
使用文件来存储每个在线用户的会话 ID 和最后活动时间,这种方法较原始,不推荐用于生产环境。
实现步骤:
- 创建一个文本文件
online.txt,存储格式为会话ID||最后活动时间戳,每行一个用户。 - 在每次请求时,将当前用户的会话信息写入文件。
- 读取文件,过滤掉超时的记录,然后统计剩余行数。
优点:无需数据库或外部服务。 缺点:文件读写有锁冲突,高并发下性能极差,数据可靠性低。
利用 WebSocket 或 Server-Sent Events (SSE)(实时性最强)
这种方式可以实现真正的“实时”在线统计,而非基于时间窗口估算。
实现步骤:
- WebSocket 服务端:使用 Ratchet、Swoole 或 Workerman 等 PHP WebSocket 库。
- 连接管理:当用户 WebSocket 连接建立时,将其加入一个在线列表(存储在内存或 Redis 中)。
- 心跳检测:定期(如每 30 秒)检测连接是否存活,断开则从列表中移除。
- 客户端:前端通过 JavaScript 建立 WebSocket 连接,并监听
onlineCount事件。
优点:真正的实时在线状态,用户体验极佳。 缺点:开发复杂度高,需要维护长连接服务端,对服务器资源消耗较大。
总结建议
| 需求场景 | 推荐方案 | 原因 |
|---|---|---|
| 小型项目、数据量小、开发快 | 数据库 | 简单可靠,易于理解 |
| 中型项目、追求性能、高并发 | Redis 有序集合 | 性能高,扩展性强 |
| 需要真正的实时在线状态 | WebSocket | 实时性最强 |
| 临时测试或极简单场景 | 文件 | 不推荐,仅用于学习 |
最佳实践: 建议采用 方案一(数据库) + 方案二(Redis 缓存) 的组合,数据库作为持久化存储,Redis 作为计数器缓存,当需要统计在线人数时,优先从 Redis 读取,同时定期(如每分钟)将 Redis 中的数据同步回数据库,避免数据库成为瓶颈。
补充说明:以上方案均假设用户已登录,如果要统计未登录的访客(游客),可以用会话唯一标识(session_id())替代用户ID,原理相同。