本文目录导读:

我来详细介绍PHP用户在线状态维护的几种方案:
基于Session的方案(最简单)
实现方式
<?php
// 使用session最后活动时间
session_start();
$_SESSION['last_activity'] = time();
// 判断用户是否在线(5分钟内活跃)
function isUserOnline($lastActivity) {
return (time() - $lastActivity) < 300;
}
// 更新在线状态
function updateOnlineStatus($userId) {
$_SESSION['user_id'] = $userId;
$_SESSION['last_activity'] = time();
}
?>
优点: 简单易实现
缺点: 无法跨设备、无法持久化、Session过期即失效
数据库方案(推荐)
数据库表结构
CREATE TABLE user_online (
user_id INT PRIMARY KEY,
last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45),
session_id VARCHAR(100),
user_agent VARCHAR(255),
INDEX idx_last_activity (last_activity)
);
核心实现代码
<?php
class UserOnlineManager {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
// 记录用户活动
public function trackActivity($userId) {
$sessionId = session_id();
$ip = $_SERVER['REMOTE_ADDR'];
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$sql = "INSERT INTO user_online (user_id, last_activity, ip_address, session_id, user_agent)
VALUES (?, NOW(), ?, ?, ?)
ON DUPLICATE KEY UPDATE
last_activity = NOW(),
ip_address = VALUES(ip_address),
session_id = VALUES(session_id)";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$userId, $ip, $sessionId, $userAgent]);
}
// 检查用户是否在线
public function isOnline($userId, $timeout = 300) {
$sql = "SELECT COUNT(*) FROM user_online
WHERE user_id = ?
AND last_activity > DATE_SUB(NOW(), INTERVAL ? SECOND)";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$userId, $timeout]);
return $stmt->fetchColumn() > 0;
}
// 获取在线用户列表
public function getOnlineUsers($timeout = 300) {
$sql = "SELECT user_id, last_activity FROM user_online
WHERE last_activity > DATE_SUB(NOW(), INTERVAL ? SECOND)";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$timeout]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 清理过期记录(定期执行)
public function cleanupExpired($timeout = 300) {
$sql = "DELETE FROM user_online
WHERE last_activity < DATE_SUB(NOW(), INTERVAL ? SECOND)";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$timeout]);
}
// 用户登出时主动删除
public function userLogout($userId) {
$sql = "DELETE FROM user_online WHERE user_id = ?";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$userId]);
}
}
?>
在应用中集成
<?php
// 在页面加载时调用
require_once 'UserOnlineManager.php';
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$onlineManager = new UserOnlineManager($pdo);
// 已登录用户每次请求都更新状态
if ($_SESSION['user_id']) {
$onlineManager->trackActivity($_SESSION['user_id']);
}
// 在需要的地方检查在线状态
if ($onlineManager->isOnline(123)) {
echo "用户在线";
} else {
echo "用户离线";
}
?>
Redis方案(高性能)
使用Redis实现
<?php
class RedisUserOnline {
private $redis;
private $prefix = 'user:online:';
public function __construct($redis) {
$this->redis = $redis;
}
// 记录用户在线
public function markOnline($userId) {
$key = $this->prefix . $userId;
return $this->redis->setex($key, 300, time());
}
// 检查用户是否在线
public function isOnline($userId) {
$key = $this->prefix . $userId;
return $this->redis->exists($key);
}
// 批量获取在线状态
public function getBulkOnline($userIds) {
$keys = array_map(function($id) {
return $this->prefix . $id;
}, $userIds);
return $this->redis->mget($keys);
}
// 获取所有在线用户(需要维护一个集合)
public function markOnlineWithTracking($userId) {
$key = $this->prefix . $userId;
$this->redis->setex($key, 300, time());
// 记录在线集合
$this->redis->sAdd('online:users', $userId);
}
// 获取在线用户数
public function getOnlineCount() {
return $this->redis->scard('online:users');
}
}
?>
实时推送方案(WebSocket)
结合Node.js + Socket.io
// client端JavaScript
const socket = io('wss://your-server.com');
// 记录用户活动
setInterval(() => {
socket.emit('heartbeat', { userId: userId });
}, 60000); // 每分钟发送一次心跳
// 实时获取在线用户
socket.on('onlineUsers', (users) => {
console.log('在线用户:', users);
});
PHP服务端处理心跳
<?php
// 通过API接口接收心跳
public function handleHeartbeat($userId) {
// 更新数据库或Redis状态
$this->trackActivity($userId);
// 广播在线状态变化(通过消息队列或WebSocket)
$broadcastData = [
'user_id' => $userId,
'status' => 'online'
];
}
?>
性能优化建议
批量更新
<?php
// 使用延迟批量更新
class BatchOnlineManager {
private $pendingUpdates = [];
public function queueActivity($userId) {
$this->pendingUpdates[$userId] = time();
}
public function flushUpdates() {
if (empty($this->pendingUpdates)) return;
$sql = "INSERT INTO user_online (user_id, last_activity) VALUES ";
$values = [];
$params = [];
foreach ($this->pendingUpdates as $userId => $time) {
$values[] = "(?, ?)";
$params[] = $userId;
$params[] = date('Y-m-d H:i:s', $time);
}
$sql .= implode(',', $values);
$sql .= " ON DUPLICATE KEY UPDATE last_activity = VALUES(last_activity)";
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$this->pendingUpdates = [];
}
public function __destruct() {
$this->flushUpdates();
}
}
?>
完整示例:综合方案
<?php
class OnlineUserService {
private $db;
private $redis;
private $timeout = 300; // 5分钟超时
public function __construct($db, $redis = null) {
$this->db = $db;
$this->redis = $redis;
}
// 核心方法:更新在线状态
public function updateStatus($userId) {
// 方案1:更新数据库
$this->updateDatabase($userId);
// 方案2:更新Redis(如果可用)
if ($this->redis) {
$this->updateRedis($userId);
}
}
private function updateDatabase($userId) {
$sql = "UPDATE users SET last_active = NOW(), is_online = 1
WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId]);
}
private function updateRedis($userId) {
$key = "user:online:{$userId}";
$this->redis->setex($key, $this->timeout, 1);
}
// 检查在线状态
public function checkOnline($userId) {
// 优先从Redis读取
if ($this->redis) {
$result = $this->redis->get("user:online:{$userId}");
if ($result !== null) {
return $result == 1;
}
}
// 回退到数据库查询
$sql = "SELECT is_online, last_active FROM users WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId]);
$user = $stmt->fetch();
if ($user && $user['is_online']) {
return (time() - strtotime($user['last_active'])) <= $this->timeout;
}
return false;
}
// 获取在线用户数量
public function getOnlineCount() {
if ($this->redis) {
// Redis方式
$keys = $this->redis->keys('user:online:*');
return count($keys);
} else {
// 数据库方式
$sql = "SELECT COUNT(*) FROM users
WHERE is_online = 1
AND last_active > DATE_SUB(NOW(), INTERVAL 5 MINUTE)";
return $this->db->query($sql)->fetchColumn();
}
}
}
?>
最佳实践建议
- 超时设置:根据业务需求设置合理的超时时间(通常3-5分钟)
- 清理机制:定期清理过期记录,防止数据膨胀
- 性能考虑:高并发场景使用Redis,低并发使用数据库
- 用户感知:在页面加载时更新状态,离开时发送离线信号
- 安全考虑:防止恶意刷在线状态,验证用户身份后再更新
- 扩展性:考虑支持多设备登录,每个设备独立维护状态
根据实际需求选择合适方案,小型应用用Session或数据库即可,大型应用建议使用Redis或WebSocket方案。