Мониторинг онлайн пользователей в PHP приложении

Раздел: Разработка на PHP -> Управление пользователями

Основной метод: хранение времени последней активности в базе данных

В качестве наиболее эффективного решения для отслеживания онлайн-пользователей используется запись в реляционной базе данных времени последнего запроса каждого пользователя. При каждом визите страницы обновляется поле last_activity. Пользователь считаётся онлайн, если его последняя активность была не более заданного таймаута (например, 5 минут).

Пример структуры таблицы users с дополнительным полем:


CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    last_activity DATETIME NULL
);

Php скрипт пользователи (php скрипт управления пользователями)

Код PHP для обновления времени при каждом запросе:


<?php
session_start();
$userId = $_SESSION['user_id'];
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('UPDATE users SET last_activity = NOW() WHERE id = ?');
$stmt->execute([$userId]);
?>

Users view login php (просмотр пользователей php)

Получение списка онлайн пользователей:


$timeout = 300; // 5 минут
$deadline = date('Y-m-d H:i:s', time() - $timeout);
$query = $pdo->query("SELECT username FROM users WHERE last_activity >= '$deadline'");
$onlineUsers = $query->fetchAll(PDO::FETCH_COLUMN);

Action profile profile php (действие профиля в php)

Типичные проблемы и решения:

  • Нагрузка на БД: при большом количестве пользователей каждое обновление может быть затратным. Решение - использовать кэширование (Redis) или агрегировать обновления через очередь.
  • Гонка состояний: два одновременных запроса могут обновить время с разницей в секунду - некритично. Для строгой точности применяется блокировка строки (FOR UPDATE).
  • Очистка устаревших записей: сами по себе они не мешают, но для экономии места можно запускать CRON-задачу: DELETE FROM users WHERE last_activity < NOW() - INTERVAL 1 DAY

Как определить онлайн пользователей через файловую систему?

Метод основан на хранении временных файлов для каждой сессии. При входе создаётся файл в каталоге /tmp/online/ с именем, соответствующим ID сессии, и временем последней активности внутри. Файлы со старым временем удаляются.


$onlineDir = '/tmp/online';
$sessionId = session_id();
file_put_contents("$onlineDir/$sessionId", time());
// очистка старых
foreach (glob("$onlineDir/*") as $file) {
    $content = file_get_contents($file);
    if (time() - $content > 300) {
        unlink($file);
    }
}

Online online user php (онлайн пользователь в php)

Проблемы: низкая производительность при большом количестве файлов, проблемы с монтированием tmpfs, отсутствие атомарности в некоторых операциях.

Как реализовать онлайн статус с помощью Redis?

Redis предоставляет структуру данных SORTED SET (отсортированное множество) с временем последней активности в качестве score.


$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$userId = 42;
$redis->zAdd('online_users', time(), $userId);

// получить онлайн (активны последние 5 минут)
$deadline = time() - 300;
$redis->zRemRangeByScore('online_users', '-inf', $deadline); // очистка устаревших
$onlineIds = $redis->zRevRangeByScore('online_users', '+inf', $deadline);
$onlineIds = array_map('intval', $onlineIds);

User new php (новый пользователь в php)

Проблемы: необходимость установки Redis, потеря данных при перезапуске (если не сохранять на диск). Решение - настройка персистентности RDB/AOF.

Как использовать сессии и таблицу session в MySQL?

При встроенном хранении сессий в БД (создана таблица sessions) можно проверять время последней активности сессии.


CREATE TABLE sessions (
    session_id VARCHAR(128) PRIMARY KEY,
    last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Обновление при каждом запросе через пользовательский обработчик сессий. Получение онлайн: SELECT COUNT(*) FROM sessions WHERE last_activity > NOW() - INTERVAL 5 MINUTE.

Проблемы: сложность реализации собственного обработчика сессий, блокировки на уровне таблицы при частых записях.

- Php user error (ошибка пользователя в php)
- User php login (логин пользователя в php)
- Groups php (группы пользователей в php)

Расширенные примеры реализации отслеживания онлайн пользователей

Пример 1. AJAX-пинг для обновления статуса

Клиент отправляет запрос каждые 60 секунд, сервер обновляет время последней активности.

Пример

// ajax_ping.php
<?php
session_start();
$userId = $_SESSION['user_id'] ?? null;
if ($userId) {
    $db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $stmt = $db->prepare('UPDATE users SET last_activity = NOW() WHERE id = ?');
    $stmt->execute([$userId]);
    echo 'ok';
} else {
    http_response_code(401);
}
?>
Пример

// JavaScript (jQuery)
setInterval(function() {
    $.get('ajax_ping.php');
}, 60000);

Пример 2. Отображение списка онлайн с именем и временем

Пример

<?php
$pdo = new PDO(...);
$timeout = 300;
$deadline = date('Y-m-d H:i:s', time() - $timeout);
$stmt = $pdo->prepare('
    SELECT username, last_activity 
    FROM users 
    WHERE last_activity >= ? 
    ORDER BY last_activity DESC
');
$stmt->execute([$deadline]);
$online = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<ul>
<?php foreach ($online as $user): ?>
    <li><?= htmlspecialchars($user['username']) ?> - <?= $user['last_activity'] ?></li>
<?php endforeach; ?>
</ul>

Результат (пример):

  • admin - 2025-02-18 14:35:12
  • user1 - 2025-02-18 14:33:40
  • user2 - 2025-02-18 14:30:15

Пример 3. Комбинирование Redis и MySQL для асинхронной записи

PHP обновляет время в Redis, а отдельный демон (или CRON) переносит данные в MySQL раз в минуту. Это снижает нагрузку на БД.

Пример

// обновление в PHP
$redis = new Redis();
$redis->connect('127.0.0.1');
$redis->zAdd('online:latest', time(), $userId);

// CRON-скрипт (запуск раз в 60 сек)
$allNew = $redis->zRangeByScore('online:latest', (time()-60), '+inf');
$pdo = new PDO(...);
$stmt = $pdo->prepare('INSERT INTO users (id, last_activity) VALUES (?, NOW()) ON DUPLICATE KEY UPDATE last_activity = NOW()');
foreach ($allNew as $uid) {
    $stmt->execute([$uid]);
}
$redis->del('online:latest');

Пример 4. Безопасное удаление устаревших записей через транзакцию

Пример

$pdo->beginTransaction();
try {
    $pdo->exec("DELETE FROM users WHERE last_activity < NOW() - INTERVAL 5 MINUTE");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    // логирование ошибки
}
// Сообщение об удалении (количество удалённых строк)
echo 'Очищено: ' . $stmt->rowCount() . ' записей';

Пример 5. Использование cookies для отслеживания гостей

Для неавторизованных пользователей можно создать временный идентификатор, хранящийся в cookie, и записывать его в таблицу online_guests.

Пример

if (!isset($_COOKIE['guest_id'])) {
    $guestId = bin2hex(random_bytes(16));
    setcookie('guest_id', $guestId, time()+3600, '/');
} else {
    $guestId = $_COOKIE['guest_id'];
}
$pdo->prepare('INSERT INTO online_guests (guest_id, last_activity) VALUES (?, NOW()) ON DUPLICATE KEY UPDATE last_activity = NOW()')
    ->execute([$guestId]);

Онлайн пользователь в PHP - comments

En
Online online user php (php)