Le travail cron de collecte de déchets d'Ubuntu pour les sessions PHP prend 25 minutes pour s'exécuter, pourquoi?

13

Ubuntu a une configuration de tâche cron qui recherche et supprime les anciennes sessions PHP:

# Look for and purge old sessions every 30 minutes
09,39 *     * * *     root   [ -x /usr/lib/php5/maxlifetime ] \
   && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 \
   -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir \
   fuser -s {} 2> /dev/null \; -delete

Mon problème est que ce processus prend très longtemps à s'exécuter, avec beaucoup d'E / S de disque. Voici mon graphique d'utilisation du processeur:

Graphique d'utilisation du processeur

Le nettoyage en cours d'exécution est représenté par les pointes sarcelles. Au début de la période, les travaux de nettoyage de PHP étaient programmés aux heures par défaut de 09 et 39 minutes. À 15h00, j'ai supprimé le temps de 39 minutes de cron, donc un travail de nettoyage deux fois plus grand s'exécute deux fois plus souvent (vous pouvez voir les pics devenir deux fois plus larges et deux fois plus fréquents).

Voici les graphiques correspondants pour le temps d'E / S:

Temps IO

Et opérations sur disque:

Opérations sur disque

Au pic où il y avait environ 14000 sessions actives, le nettoyage peut être exécuté pendant 25 minutes complètes, utilisant apparemment 100% d'un cœur du CPU et ce qui semble être 100% des E / S du disque pour toute la période. Pourquoi est-il si gourmand en ressources? Un lsrépertoire de session ne /var/lib/php5prend qu'une fraction de seconde. Alors, pourquoi faut-il 25 minutes pour couper les anciennes sessions? Puis-je faire quelque chose pour accélérer cela?

Le système de fichiers de cet appareil est actuellement ext4 et fonctionne sur Ubuntu Precise 12.04 64 bits.

EDIT: je soupçonne que la charge est due au processus inhabituel "fuser" (car je m'attends à ce qu'un simple rmsoit un sacrément plus rapide que les performances que je vois). Je vais supprimer l'utilisation de l'unité de fusion et voir ce qui se passe.

thenickdude
la source
Combien de trafic votre site Web génère-t-il pour générer autant de sessions?
Michael Hampton

Réponses:

9

La suppression de fuserdevrait aider. Ce travail exécute une fusercommande (vérifiez si un fichier est actuellement ouvert) pour chaque fichier de session trouvé , ce qui peut facilement prendre plusieurs minutes sur un système occupé avec 14 000 sessions. C'était un bogue Debian (Ubuntu est basé sur Debian).

Au lieu de memcached, vous pouvez également essayer d'utiliser tmpfs (un système de fichiers en mémoire) pour les fichiers de session. Comme memcached, cela invaliderait les sessions au redémarrage (cela peut être contourné en sauvegardant ce répertoire quelque part dans le script d'arrêt et en restaurant dans le script de démarrage), mais sera beaucoup plus facile à configurer. Mais cela ne résoudra pas le fuserproblème.

Tometzky
la source
Il semble que le bug dans l'unité de fusion était qu'une version antérieure ait été bifurquée mais n'a jamais été récoltée à la fin, laissant des milliers de fuserprocessus dans un état zombie consommant de la mémoire, ce qui entraîne un crash du serveur. Je pense que cela a déjà été corrigé dans la version de psmisc que j'utilise.
thenickdude
Voilà un autre bug. Vous avez un problème simple de démarrage de milliers de fuserprocessus, qui doivent tous rechercher l'ensemble /proc/des fichiers ouverts.
Tometzky
9

Félicitations d'avoir un site Web populaire et d'avoir réussi à le faire fonctionner sur une machine virtuelle pendant tout ce temps.

Si vous enregistrez vraiment deux millions de pages vues par jour, alors vous allez empiler BEAUCOUP de sessions PHP dans le système de fichiers, et elles vont prendre beaucoup de temps à supprimer, que vous utilisiez fuserou rmou aspirateur.

À ce stade, je vous recommande de rechercher d'autres moyens de stocker vos sessions:

  • Une option consiste à stocker les sessions dansmemcached . C'est rapide comme l'éclair, mais si le serveur plante ou redémarre, toutes vos sessions sont perdues et tout le monde est déconnecté.
  • Vous pouvez également stocker des sessions dans une base de données. Ce serait un peu plus lent que Memcached, mais la base de données serait persistante et vous pourriez effacer les anciennes sessions avec une simple requête SQL. Pour implémenter cela, cependant, vous devez écrire un gestionnaire de session personnalisé .
Michael Hampton
la source
Memcached est certainement une option, bien qu'il devrait s'agir d'un pool distinct de notre instance memcached principale, sinon les sessions seraient expulsées de manière aléatoire de notre pression de cache. Cependant, je ne suis pas convaincu que la suppression de 14 000 fichiers devrait prendre 25 minutes. Cela me semble trop lent. Je vais attendre quelques heures et voir à quoi ressemblent les performances d'un simple rm.
thenickdude
Sans en savoir plus sur votre architecture globale, j'hésite à vous recommander l'un sur l'autre.
Michael Hampton
Vous pouvez regrouper les serveurs Memcached pour la redondance en définissant memcache.session_redundancy = 2. Voir serverfault.com/questions/164350/… . Redis est une bonne option si vous êtes préoccupé par la persistance et beaucoup plus rapide que les magasins de bases de données SQL.
jfountain
4

Ainsi, les options de stockage de session Memcached et de base de données suggérées par les utilisateurs ici sont à la fois de bons choix pour augmenter les performances, chacune avec ses propres avantages et inconvénients.

Mais en testant les performances, j'ai constaté que le coût énorme des performances de cette maintenance de session est presque entièrement dû à l'appel à fuserla tâche cron. Voici les graphiques de performances après le retour au travail cron Natty / Oneiric qui utilise rmau lieu de fusercouper les anciennes sessions, le basculement se produit à 2h30.

l'utilisation du processeur

Temps d'E / S écoulé

Opérations sur disque

Vous pouvez voir que la dégradation périodique des performances provoquée par le nettoyage de session PHP d'Ubuntu est presque entièrement supprimée. Les pics montrés dans le graphique des opérations sur disque sont maintenant beaucoup plus petits en amplitude, et à peu près aussi maigres que ce graphique peut éventuellement mesurer, montrant une petite interruption courte où les performances du serveur étaient auparavant considérablement dégradées pendant 25 minutes. L'utilisation supplémentaire du processeur est entièrement éliminée, il s'agit désormais d'un travail lié aux E / S.

(un travail d'E / S non lié s'exécute à 05h00 et le travail du processeur s'exécute à 7h40, ce qui provoque leurs propres pics sur ces graphiques)

Le travail cron modifié que j'exécute actuellement est le suivant:

09 *     * * *     root   [ -x /usr/lib/php5/maxlifetime ] && \
   [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 \
   -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) -print0 \
   | xargs -n 200 -r -0 rm
thenickdude
la source
-print0 | xargs ...n'est pas nécessaire - vous pouvez simplement partir de -deletelà. Mais cela fonctionnera dans les deux sens avec une vitesse comparable.
Tometzky
1

Je suis tombé sur ce post en faisant des recherches sur les sessions. Bien que la réponse acceptée soit très bonne (et l'appel de l'unité de fusion a été supprimé du script gc depuis un certain temps), je pense qu'il vaut la peine de noter quelques autres considérations si quelqu'un d'autre rencontre un problème similaire.

Dans le scénario décrit, l'OP utilisait ext4. Les répertoires dans les fichiers de stockage ext4 stockent dans un format de base de données htree - ce qui signifie qu'il y a un impact négligeable dans la conservation de nombreux fichiers dans un seul répertoire par rapport à leur distribution dans plusieurs répertoires. Ce n'est pas le cas de tous les systèmes de fichiers. Le gestionnaire par défaut en PHP vous permet d'utiliser plusieurs sous-répertoires pour les fichiers de session (mais notez que vous devez vérifier que le processus de contrôle se reproduit dans ces répertoires - le travail cron ci-dessus ne le fait pas).

Une grande partie du coût de l'opération (après la suppression de l'appel à l'unité de fusion) provient de la recherche de fichiers qui ne sont pas encore périmés. L'utilisation (par exemple) d'un seul niveau de sous-répertoires et de 16 tâches cron à la recherche dans chaque sous-répertoire (0 /, 1 /, ... d /, e /, f /) atténuera les charges générées.

L'utilisation d'un gestionnaire de session personnalisé avec un substrat plus rapide vous aidera - mais il y a beaucoup de choix (memcache, redis, socket de gestionnaire mysql ...) en laissant de côté la plage de qualité de ceux publiés sur Internet, que vous choisissez dépend de l'exact exigences concernant votre application, votre infrastructure et vos compétences, sans oublier qu'il existe fréquemment des différences dans la gestion de la sémantique (notamment le verrouillage) par rapport au gestionnaire par défaut.

symcbean
la source
0

Avec ce type de trafic, vous ne devriez pas mettre de sessions sur un disque. Vous devriez utiliser quelque chose comme memcache. Tout ce que vous avez à faire est de configurer php et aucun changement de code ne sera nécessaire. Voir par exemple

http://www.dotdeb.org/2008/08/25/storing-your-php-sessions-using-memcached/

La raison pour laquelle cela prend si longtemps est due à la quantité énorme de fichiers qu'il doit trier pour voir lesquels peuvent être supprimés. Memcache peut expirer automatiquement ces derniers en fonction de la durée de votre session que vous avez définie dans votre code.

Mike
la source