La forma en que lo hago es hacer ping periódicamente a un controlador especial usando AJAX (una vez cada 30 segundos). Son muchas solicitudes, así que en lugar de actualizar la tabla de usuarios en línea cada vez con la última visita, estoy actualizando Memcache. Solo actualizo el registro de la base de datos cuando está 5 minutos por detrás del registro de Memcache (puede elegir un retraso mayor o menor dependiendo de la carga). Luego, un trabajo cron elimina los registros obsoletos de la tabla de usuarios en línea.
Para verificar si un usuario específico está en línea, simplemente verifico su registro de Memcache. El error nunca es más de 30 segundos. La base de datos nunca se atrasa más de 5 minutos, por lo que los resultados de la base de datos también son bastante precisos.
También utilizo estas solicitudes periódicas para enviar eventos al usuario.