Comment supprimer des millions de fichiers sans déranger le serveur


11

Je voudrais supprimer un répertoire de cache nginx, que j'ai rapidement purgé par:

mv cache cache.bak
mkdir cache
service nginx restart

Maintenant, j'ai un cache.bakdossier qui contient 2 millions de fichiers. Je voudrais le supprimer, sans déranger le serveur.

Une simple rm -rf cache.bakcorbeille le serveur, même la réponse HTTP la plus simple prend 16 secondes pendant que rm fonctionne, donc je ne peux pas faire ça.

J'ai essayé ionice -c3 rm -rf cache.bak, mais ça n'a pas aidé. Le serveur a un disque dur, pas un SSD, probablement sur un SSD, cela pourrait ne pas être un problème.

Je crois que la meilleure solution serait une sorte de limitation, comme le fait le gestionnaire de cache intégré de nginx.

Comment résoudriez-vous cela? Existe-t-il un outil capable de faire exactement cela?

ext4 sur Ubuntu 16.04


1
Comment avez-vous récupéré de "rm -rf cache.bak"? Il semble que nginx était en cours d'exécution lorsque vous avez effectué le changement de nom, il a donc pu conserver les descripteurs de fichiers et même basculer vers le nouveau répertoire. Je pense que vous devez fermer complètement nginx, supprimer le cache, puis redémarrer.
Jan Steinman

6
À l'avenir, veuillez coller votre cache sur un système de fichiers distinct. De cette façon, vous pouvez simplement supprimer ce système de fichiers, ce qui est beaucoup plus rapide que d'essayer de supprimer des millions de fichiers. J'ai appris cela à la dure il y a quelques années avec un répertoire de spool hylafax contenant des millions de fichiers.
Dennis Kaarsemaker

Avez-vous essayé de courir rmavec nice ?
Vladislav Rastrusny

Essayez rsync pour supprimer rapidement - réponses à un cas similaire - unix.stackexchange.com/questions/37329/…
kawu

Merci pour tous les commentaires, j'ai résumé mes résultats à la réponse écrite.
hyperknot

Réponses:


9

Créez un script bash comme ceci:

#!/bin/bash
rm -- "$*"
sleep 0.5

Enregistrez-le avec le nom deleter.shpar exemple. Exécutez chmod u+x deleter.shpour le rendre exécutable.

Ce script supprime tous les fichiers qui lui sont passés en tant qu'arguments, puis s'endort 0,5 seconde.

Ensuite, vous pouvez exécuter

find cache.bak -print0 | xargs -0 -n 5 deleter.sh

Cette commande récupère une liste de tous les fichiers dans cache.bak et transmet les cinq noms de fichiers à la fois au script de suppression.

Ainsi, vous pouvez ajuster le nombre de fichiers supprimés à la fois et le délai entre chaque opération de suppression.


Merci pour cette solution, je l'ai incluse dans mon résumé global. Une question cependant, comment cela gère-t-il les grands ns? J'ai généralement eu des problèmes avec le caractère * dans les grands répertoires donnant des erreurs, n'est-ce pas le cas ici?
hyperknot

xargscomprend la taille maximale d'une ligne de commande et essaie de ne pas la dépasser par défaut. Celui-ci a des limites supplémentaires de pas plus de 5 chemins à la fois.
BowlOfRed

1
Sachez simplement qu'au rythme de 10 fichiers par seconde, il faudra 55 heures pour supprimer 2 millions de fichiers.
Andrew Henle

4

Vous devriez envisager d'enregistrer votre cache sur un système de fichiers distinct que vous pouvez monter / démonter comme quelqu'un l'a indiqué dans les commentaires. Jusqu'à ce que vous le fassiez, vous pouvez utiliser cette doublure en /usr/bin/find /path/to/files/ -type f -print0 -exec sleep 0.2 \; -exec echo \; -deletesupposant que votre recherche binaire se trouve sous / usr / bin et que vous souhaitez voir la progression à l'écran. Ajustez le sommeil en conséquence, afin de ne pas surcharger votre disque dur.


On n'a pas besoin -print0ici, car vous ne canalisez pas la sortie de findn'importe où.
Tero Kilkanen

Vous pourriez être intéressé par ce qui se passe. Appelez cela de la paranoïa, mais je veux toujours être sûr de supprimer les bons fichiers.
Alex

Ah vrai, je ne décodais pas la commande correctement, ma mauvaise.
Tero Kilkanen

3

Vous voudrez peut-être essayer ionice sur un script consommant la sortie d'une commande find. Quelque chose comme ceci:

ionice -c3 $(
for file in find cache.bak -type f; do
    rm $file
done
for dir in find cache.bak -depthe -type d -empty; do
    rmdir $dir
done
)

Selon le système de fichiers, chaque suppression de fichier peut entraîner la réécriture de tout le répertoire. Pour les grands répertoires, cela peut être un succès. Des mises à jour supplémentaires sont requises pour la table inode et éventuellement une liste d'espace libre.

Si le système de fichiers a un journal, les modifications sont écrites dans le journal; appliqué; et retiré du journal. Cela augmente les exigences d'E / S pour les activités gourmandes en écriture.

Vous souhaiterez peut-être utiliser un système de fichiers sans journal pour le cache.

Au lieu d'ionice, vous pouvez utiliser une commande de veille pour limiter les actions. Cela fonctionnera même si ionice ne fonctionne pas, mais il faudra beaucoup de temps pour supprimer tous vos fichiers.


2

J'ai obtenu de nombreuses réponses / commentaires utiles ici, que je voudrais conclure et montrer également ma solution.

  1. Oui, la meilleure façon d' empêcher qu'une telle chose ne se produise est de conserver le répertoire cache sur un système de fichiers séparé. Nuking / formatage rapide d'un système de fichiers prend toujours quelques secondes (peut-être quelques minutes) au maximum, sans rapport avec le nombre de fichiers / répertoires qui y étaient présents.

  2. Les solutions ionice/ nicen'ont rien fait, car le processus de suppression n'a en fait provoqué pratiquement aucune E / S. Ce qui a causé les E / S, c'est que je pense que les files d'attente / tampons au niveau du noyau / système de fichiers se remplissent lorsque les fichiers sont supprimés trop rapidement par le processus de suppression.

  3. La façon dont je l'ai résolu est similaire à la solution de Tero Kilkanen, mais ne nécessitait pas d'appeler un script shell. J'ai utilisé le --bwlimitcommutateur intégré de rsync pour limiter la vitesse de suppression.

La commande complète était:

mkdir empty_dir
rsync -v -a --delete --bwlimit=1 empty_dir/ cache.bak/

Maintenant, bwlimit spécifie la bande passante en kilo-octets, qui dans ce cas s'applique au nom de fichier ou au chemin des fichiers. En le définissant sur 1 Ko / s, il supprimait environ 100 000 fichiers par heure, soit 27 fichiers par seconde. Les fichiers avaient des chemins relatifs comme cache.bak/e/c1/db98339573acc5c76bdac4a601f9ec1e, qui sont de 47 caractères de long, donc cela donnerait 1000/47 ~ = 21 fichiers par seconde, donc un peu similaire à ma supposition de 100 000 fichiers par heure.

Maintenant pourquoi --bwlimit=1? J'ai essayé différentes valeurs:

  • 10000, 1000, 100 -> ralentissement du système comme avant
  • 10 -> le système fonctionne assez bien pendant un certain temps, mais produit des ralentissements partiels une fois par minute environ. Temps de réponse HTTP toujours <1 sec.
  • 1 -> aucun ralentissement du système. Je ne suis pas pressé et 2 millions de fichiers peuvent être supprimés en <1 jour de cette façon, alors je le choisis.

J'aime la simplicité de la méthode intégrée de rsync, mais cette solution dépend de la longueur du chemin relatif. Pas un gros problème car la plupart des gens trouveraient la bonne valeur par essais et erreurs.


Et maintenant, je suis curieux de savoir quel serait l'effet du disque si vous faisiez quelque chose comme "mv cache.dir-old / dev / null"
ivanivan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.