Pourquoi la suppression de fichiers par nom est-elle douloureusement lente et aussi exceptionnellement rapide?

11

Faux pas: La méthode "rapide" que je mentionne ci-dessous n'est pas 60 fois plus rapide que la lente. C'est 30 fois plus rapide. Je vais blâmer l'erreur sur l'heure (3 heures du matin n'est pas mon meilleur moment de la journée pour une réflexion claire :) ..

Mise à jour: j'ai ajouté un résumé des temps de test (ci-dessous).
Il semble y avoir deux problèmes liés au facteur vitesse:

  • Le choix de la commande utilisée (comparaisons temporelles indiquées ci-dessous)
  • La nature d'un grand nombre de fichiers dans un répertoire ... Il semble que "gros c'est mauvais". Les choses ralentissent de manière disproportionnée à mesure que les chiffres augmentent.

Tous les tests ont été effectués avec 1 million de fichiers.
(les temps réel, utilisateur et sys sont dans les scripts de test)
Les scripts de test peuvent être trouvés sur paste.ubuntu.com

#
# 1 million files           
# ===============
#
#  |time   |new dir   |Files added in  ASCENDING order  
#  +----   +-------   +------------------------------------------------- 
#   real    01m 33s    Add files only (ASCENDING order) ...just for ref.
#   real    02m 04s    Add files, and make 'rm' source (ASCENDING order) 
#                      Add files, and make 'rm' source (DESCENDING order) 
#   real    00m 01s    Count of filenames
#   real    00m 01s    List of filenames, one per line
#   ----    -------    ------
#   real    01m 34s    'rm -rf dir'
#   real    01m 33s    'rm filename' via rm1000filesPerCall   (1000 files per 'rm' call)
#   real    01m 40s    'rm filename' via  ASCENDING algorithm (1000 files per 'rm' call)
#   real    01m 46s    'rm filename' via DESCENDING algorithm (1000 files per 'rm' call)
#   real    21m 14s    'rm -r dir'
#   real    21m 27s    'find  dir -name "hello*" -print0 | xargs -0 -n 1000 rm'
#   real    21m 56s    'find  dir -name "hello*" -delete'
#   real    23m 09s    'find  dir -name "hello*" -print0 | xargs -0 -P 0 rm'
#   real    39m 44s    'rm filename' (one file per rm call) ASCENDING
#   real    47m 26s    'rm filename' (one file per rm call) UNSORTED
#                                                       

J'ai récemment créé et supprimé 10 millions de fichiers de test vides. Supprimer des fichiers nom par nom (c'est-à-dire rm filename), j'ai découvert à la dure qu'il y a une énorme différence de temps entre 2 méthodes différentes ...

Les deux méthodes utilisent exactement la même rm filenamecommande.

Mise à jour: il s'avère que les commandes n'étaient pas exactement les mêmes ... L'un d'eux envoyait 1000 noms de fichiers à la fois à 'rm' ... C'était un problème d'extension d'accolade du shell où je pensais que chaque nom de fichier était écrit au fichier d'alimentation sur une ligne à part, mais en réalité, il était de 1000 par ligne

Les noms de fichiers sont fournis via un «fichier d'alimentation» dans une while readboucle.
Le fichier d'alimentation est la sortie de ls -1 -f
Les méthodes sont identiques à tous égards, sauf pour une chose:

  • la méthode lente utilise le fichier d'alimentation non trié directement depuisls -1 -f
  • la méthode rapide utilise une version triée de ce même fichier non trié

Je ne sais pas si le tri est le problème ici, ou est-ce peut-être que le fichier d'alimentation trié correspond simplement à la séquence dans laquelle les fichiers ont été créés (j'ai utilisé un algorithme simple croissant)

Pour 1 million de fichiers, la méthode rapide rm filename est 60 fois plus rapide que la méthode lente ... encore une fois, je ne sais pas s'il s'agit d'un problème de "tri" ou d'un problème de table de hachage en arrière-plan ... Je soupçonne ce n'est pas un simple problème de tri, car pourquoi me ls -1 -fdonnerait-il intentionnellement une liste non triée d'une séquence de noms de fichiers "triée" fraîchement ajoutée ...

Je me demande simplement ce qui se passe ici, donc il ne me faut pas des jours (oui des jours) pour supprimer les 10 millions de fichiers suivants :) .... Je dis "jours" parce que j'ai essayé tant d'alternatives, et le les temps impliqués augmentent de manière disproportionnée par rapport au nombre de fichiers impliqués .. donc je n'ai testé que 1 million en détail

BTW: La suppression des fichiers via la "liste triée" des noms est en fait plus rapide que rm -rfd'un facteur 2.
et: rm -rétait 30 fois plus lente que la méthode "liste triée"

... mais est "trié" le problème ici? ou est-il plus lié à une méthode de stockage de hachage (ou autre) utilisée par ext4?

La chose qui me laisse perplexe, c'est que chaque appel à rm filenameest sans rapport avec le précédent .. (enfin, au moins c'est de cette façon du point de vue 'bash')

J'utilise le lecteur Ubuntu / bash / 'ext4' / SATA II.

Peter.O
la source
1
Vous le faites mal! (tm) Avez-vous déjà entendu parler de find -delete?
alex
Vos 2 tests commencent dans des conditions inégales (je ne prétends pas que ce soit important en effet): l'un lit les noms de fichiers dans un fichier, et l'autre lit les noms de fichiers dans un fichier créé (trié) immédiatement avant le test. Il se peut que le fichier mis en cache dans le deuxième cas en joue (ou peut-être pas, qui sait). Pour que les tests soient dans des conditions plus égales, vous devriez peut-être faire un simple catà un nouveau fichier avant le 1er test - à la place sortavant le 2e test.
imz - Ivan Zakharyaschev
Et je vous recommande de présenter vos observations et votre question de façon plus claire. S'il vous plaît, une chose à la fois: comparez seulement 2 cas en une seule question, mettez les deux cas importants au premier plan, tous les autres ne sont que des informations de base; veuillez le préciser. Ne mélangez pas plusieurs observations dans une seule publication, s'il vous plaît.
imz - Ivan Zakharyaschev
La présentation du temps système et espace utilisateur à partir de votre ordinateur peut également être importante pour résoudre le casse-tête, veuillez donc les inclure dans votre question. Lequel d'entre eux fait la grande différence dans vos tests?
imz - Ivan Zakharyaschev
1
L'optimisation prématurée est la racine de tout Mal. :) Quand supprimerez-vous jamais 10 millions de fichiers? 100 000 par seconde me semble assez rapide (pour ruiner votre système).
utilisateur inconnu

Réponses:

2

rm -r devrait être lent car récursif. Une première traversée en profondeur doit être effectuée sur la structure du répertoire.

Maintenant, comment avez-vous créé 10 millions de fichiers? avez-vous utilisé un script qui boucle dans un certain ordre? 1.txt, 2.txt, 3.txt ... si oui, ces fichiers peuvent également être alloués dans le même ordre dans des blocs contigus dans hdd.so, la suppression dans le même ordre sera plus rapide.

"ls -f" activera -aU qui liste dans l'ordre du répertoire qui est à nouveau récursif.

rajaganesh87
la source
1
McAlot: Je ne vois pas comment «récursif» importerait dans ce cas , car il n'y a pas de sous-répertoires impliqués ... Oui, j'ai utilisé «1.txt, 2.txt, 3.txt». Peut-être qu'il y en a plusieurs les choses interagissent: par exemple, pourquoi cela prend-il seulement 1min 30s pour créer 1 million de fichiers, mais il faut 7m 10s pour créer 2 millions. et après les avoir supprimés, recréer le 1 million prend beaucoup plus de temps (9m 30s) son étrange; lentement tout d'un coup. Cela s'est déjà produit auparavant. Je pense que (?) la suppression du répertoire l'a corrigé. Y a-t-il un démon de fichier impliqué (nautilus; localiser) peut-être? À suivre ...
Peter.O
En général, les systèmes de fichiers ne sont pas optimisés pour traiter un grand nombre de fichiers dans le même répertoire. Je ne connais pas spécifiquement ext4, mais pour les autres formats, les entrées du répertoire ont simplement été marquées comme inutilisées lorsque les fichiers ont été supprimés. Cela signifie qu'ils doivent toujours être ignorés lors des opérations dans le répertoire. Cela expliquerait le comportement que vous voyez.
KeithB
1
J'ai supprimé le répertoire «maintenant plus lent» et utilisé un nom différent pour un nouveau répertoire. Le temps de créer 1 million de fichiers est maintenant revenu à 1m 33s (vs 9m 30s lorsque le répertoire "contient" 2 millions de fichiers supprimés, le premier million ayant le même nom que le nouveau million ajouté) ... intéressant, et il correspond à votre commentaire "... juste marqué comme inutilisé" ... y arriver; ça commence à avoir du sens :)
Peter.O
@ fred.bear Mon mauvais, je ne connaissais vraiment pas la hiérarchie réelle et ma réponse était une supposition. votre test met également l'accent sur les métadonnées mais pas sur les fichiers réels car ce sont des fichiers vides. La meilleure façon de comparer ce type de problème est de récupérer des fichiers depuis / var ou le cache du serveur Web. Quoi qu'il en soit, votre test semble également intéressant, pouvez-vous essayer de supprimer avec deux méthodes répertoriées dans des répertoires différents ... dites comme /sample1/1.txt,2.txt ... et /sample2/1.txt,2.txt ..
rajaganesh87
@ Mr.Confused.A.Lot ... Merci pour votre aide. Votre explication m'a aidé à mieux comprendre le système de fichiers et certains de ses modes de fonctionnement ... J'ai maintenant une idée raisonnable de ce qui causait les différents problèmes de vitesse ... Certains n'étaient que des choix de commandes bash, et d'autres étaient simplement des problèmes de système de fichiers ( Il me reste une nouvelle devise: "big is bad" pour les répertoires ... (pour certaines actions, au moins) ...
Peter.O
2

Vous devez optimiser la structure du fichier. Donc au lieu de

for i in $(seq 1 1000); do touch file.$i; done

faire quelque chose de plus intelligent comme (supposé bash):

function bucklocate() 
{ 
    hash=$(echo -n "$1"|md5sum|cut -f1); 
    echo -n "${hash:1:1}/${hash:7:1}/${hash:9:2}/$1"; 
}

hexdig="{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}"
eval mkdir -p $hexdig/$hexdig/$hexdig$hexdig


for i in $(seq 1 1000); do touch $(bucklocate file.$i); done

Maintenant, cet exemple est plutôt lent en raison de l'utilisation de md5sum [1], utilisez quelque chose comme le suivant pour une réponse beaucoup plus rapide, tant que vous n'avez pas besoin de noms de fichiers particuliers, les doublons ne sont pas un problème et il n'y a pas besoin d'un hachage répétable d'un certain nom :)

mkdir -pv {0,1,2,3,4,5,6}/{0,1,2,3,4,5,6,7,8,9,10,12}
for  a in $(seq 1 100); do i=$RANDOM; echo touch "$(($i%7))/$(($i%13))/file.$i"; done

Bien sûr, ce sont tous des concepts empruntés à des tables de hachage

sehe
la source
Je pense que vous dites "utilisez des répertoires plus petits" ... C'est une idée intéressante; un SGBD fait maison qui crée un arbre à partir d'un groupe de fichiers "sans arbre" ". Certains pourraient l'appeler planification anticipée :) ... Si cela fonctionne (et c'est probablement le cas), alors c'est une bonne idée ! :) ... Je commence à avoir l'idée que "grand est mauvais" quand il s'agit du nombre de fichiers dans un répertoire (pour ext4 au moins) ... Vous avez présenté une solution de contournement préventive (+1) et je '
Je me fais
Eh oui désolé de ne pas être plus explicite sur l'idée de dirs de petite tenue
sehe