Contexte: serveur physique, âgé d'environ deux ans, disques SATA à 7 200 tr / min connectés à une carte RAID 3Ware, ext3 FS monté avec noatime et data = commandé, pas sous une charge folle, noyau 2.6.18-92.1.22.el5, temps de disponibilité 545 jours . Directory ne contient aucun sous-répertoire, mais des millions de petits fichiers (~ 100 octets), avec des fichiers plus volumineux (quelques Ko).
Nous avons un serveur qui est devenu un peu coucou au cours des derniers mois, mais nous ne l’avons remarqué que l’autre jour quand il a commencé à être incapable d’écrire dans un répertoire car il contenait trop de fichiers. Plus précisément, il a commencé à générer cette erreur dans / var / log / messages:
ext3_dx_add_entry: Directory index full!
Le disque en question a beaucoup d’inodes restants:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Donc, je suppose que cela signifie que nous atteignons la limite du nombre d'entrées pouvant être dans le fichier répertoire lui-même. Aucune idée du nombre de fichiers, mais il ne peut pas y avoir plus de trois millions à peu près, comme vous pouvez le constater. Ce n'est pas bien, remarquez! Mais c’est la première partie de ma question: quelle est exactement cette limite supérieure? Est-ce accordable? Avant que je me crié dessus, je veux le régler vers le bas ; cet énorme répertoire a causé toutes sortes de problèmes.
Quoi qu'il en soit, nous avons repéré le problème dans le code qui générait tous ces fichiers, et nous l'avons corrigé. Maintenant, je suis coincé avec la suppression du répertoire.
Quelques options ici:
rm -rf (dir)
J'ai essayé ceci en premier. J'ai abandonné et je l'ai tué après une journée et demie sans impact perceptible.
- unlink (2) sur le répertoire: Cela mérite certainement d'être pris en compte, mais la question est de savoir s'il serait plus rapide de supprimer les fichiers du répertoire via fsck que de supprimer via unlink (2). C'est-à-dire que, d'une manière ou d'une autre, je dois marquer ces inodes comme inutilisés. Bien entendu, cela suppose que je puisse dire à fsck de ne pas supprimer les entrées dans les fichiers de / lost + found; sinon, je viens de déplacer mon problème. En plus de toutes les autres préoccupations, après avoir lu un peu plus à ce sujet, il s'avère que je devrais probablement appeler des fonctions FS internes, car aucune des variantes unlink (2) que je peux trouver ne me permettrait de supprimer allègrement. un répertoire avec des entrées. Caca.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
C'est en fait la version abrégée; le vrai que je suis en train d’exécuter, qui ajoute quelques rapports de progression et un arrêt net lorsque nous sommes à court de fichiers à supprimer, est le suivant:
exportation i = 0; time (while [true]; do ls -Uf | tête -n 3 | grep -qF '.png' || Pause; ls -Uf | tête -n 10000 | xargs rm -f 2> / dev / null; exportation i = $ (($ i + 10000)); echo "$ i ..."; terminé )
Cela semble fonctionner plutôt bien. Au moment où j'écris ces lignes, il a supprimé 260 000 fichiers au cours des trente dernières minutes.
- Comme mentionné ci-dessus, la limite d'entrée par répertoire est-elle réglable?
- Pourquoi a-t-il fallu "vrais 7m9.561s / utilisateur 0m0.001s / sys 0m0.001s" pour supprimer un fichier qui était le premier dans la liste renvoyée par
ls -U
, et il a fallu peut-être dix minutes pour supprimer les 10 000 premières entrées avec le commande en n ° 3, mais maintenant il roule assez bien? D'ailleurs, il en a supprimé 260 000 en une trentaine de minutes, mais il lui faut maintenant quinze minutes supplémentaires pour en supprimer 60 000 de plus. Pourquoi les énormes sautes de vitesse? - Y a-t-il une meilleure façon de faire ce genre de chose? Pas stocker des millions de fichiers dans un répertoire; Je sais que c'est idiot, et cela ne serait pas arrivé sous ma montre. Googler le problème et regarder à travers SF et SO offre beaucoup de variations sur ce
find
qui ne va pas être beaucoup plus rapide que mon approche pour plusieurs raisons évidentes. Mais l'idée de delete-via-fsck a-t-elle des jambes? Ou quelque chose d'autre entièrement? Je suis impatient d'entendre des réflexions originales (ou dans la boîte non connue).
Sortie du script final !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Donc, trois millions de fichiers supprimés en un peu plus de quatre heures.
rm -rfv | pv -l >/dev/null
. pv devrait être disponible dans le référentiel EPEL .Réponses:
L’
data=writeback
option de montage mérite d’être essayée afin d’empêcher la journalisation du système de fichiers. Cela ne doit être effectué que pendant la période de suppression. Cependant, le serveur est en cours d'arrêt ou redémarré pendant l'opération de suppression.Selon cette page ,
L'option est définie dans
fstab
ou pendant l'opération de montage, en remplaçantdata=ordered
pardata=writeback
. Le système de fichiers contenant les fichiers à supprimer doit être remonté.la source
commit
option suivante : "Cette valeur par défaut (ou toute valeur basse) nuira aux performances, mais elle est bonne pour la sécurité des données. Le réglage sur 0 aura le même effet que de le laisser au réglage par défaut (5 secondes). ). Le réglage sur de très grandes valeurs améliorera les performances ".data=writeback
met toujours les métadonnées en revue avant de les écrire dans le système de fichiers principal. Si je comprends bien, cela n’impose tout simplement pas d’ordre entre l’écriture d’une carte de l’étendue et l’écriture de données dans ces domaines. Peut-être y at-il d’autres contraintes d’ordre qui s’atténueront si vous en tirez un avantage. Bien entendu, un montage sans journal pourrait être encore plus performant. (Cela pourrait laisser les changements de métadonnées se produire dans la RAM, sans rien avoir besoin d’être sur le disque avant la fin de l’opération de dissociation).Une cause majeure de ce problème est la performance ext3 avec des millions de fichiers, mais la cause première de ce problème est différente.
Quand un répertoire doit être répertorié, readdir () est appelé sur le répertoire, ce qui donne une liste de fichiers. readdir est un appel posix, mais le véritable appel système Linux utilisé ici s'appelle 'getdents'. Getdents liste les entrées du répertoire en remplissant un tampon avec des entrées.
Le problème tient principalement au fait que readdir () utilise une taille de tampon fixe de 32 Ko pour extraire des fichiers. Lorsqu'un répertoire devient de plus en plus grand (la taille augmente à mesure que des fichiers sont ajoutés), ext3 lit de plus en plus lentement pour extraire des entrées et la taille de mémoire tampon supplémentaire de 32 Ko de readdir est suffisante pour inclure une fraction des entrées du répertoire. Readdir est ainsi mis en boucle, invoquant l'appel système coûteux, encore et encore.
Par exemple, sur un répertoire de test que j'ai créé et contenant plus de 2,6 millions de fichiers, l'exécution de "ls -1 | wc-l" affiche une grande sortie strace de nombreux appels système getdent.
De plus, le temps passé dans ce répertoire était significatif.
La méthode pour rendre ce processus plus efficace consiste à appeler getdents manuellement avec un tampon beaucoup plus volumineux. Cela améliore considérablement les performances.
Maintenant, vous n'êtes pas censé appeler getdents vous-même manuellement, il n'y a donc pas d'interface pour l'utiliser normalement (consultez la page de manuel relative à getdents pour voir!), Mais vous pouvez l' appeler manuellement et rendre votre appel d'appel système plus efficace.
Cela réduit considérablement le temps nécessaire pour récupérer ces fichiers. J'ai écrit un programme qui fait ça.
Bien que cela ne combat pas le problème fondamental sous-jacent (beaucoup de fichiers, dans un système de fichiers qui fonctionne mal). Cela risque d'être beaucoup, beaucoup plus rapide que la plupart des alternatives affichées.
En prévision, il convient de supprimer le répertoire affecté et de le refaire ensuite. La taille des répertoires n’augmente jamais et peut rester médiocre, même avec quelques fichiers, à cause de la taille du répertoire.
Edit: j'ai nettoyé cela un peu. Ajout d'une option pour vous permettre de supprimer sur la ligne de commande au moment de l'exécution et suppression d'un tas de trucs de promenade dans les arbres qui, honnêtement, rétrospectivement était au mieux discutable. Aussi a été montré pour produire une corruption de mémoire.
Vous pouvez maintenant faire
dentls --delete /my/path
Nouveaux résultats Basé sur un répertoire de 1,82 million de fichiers.
Était un peu surpris cela fonctionne toujours si bien!
la source
[256]
probablement[FILENAME_MAX]
, et deux, mon Linux (2.6.18 == CentOS 5.x) ne semble pas inclure une entrée d_type dans dirent (au moins selon getdents (2)).Serait-il possible de sauvegarder tous les autres fichiers de ce système de fichiers dans un emplacement de stockage temporaire, de reformater la partition, puis de restaurer les fichiers?
la source
Il n'y a pas de limite de fichiers par répertoire dans ext3, mais uniquement la limite d'inode du système de fichiers (je pense cependant qu'il existe une limite quant au nombre de sous-répertoires).
Vous pouvez toujours avoir des problèmes après la suppression des fichiers.
Lorsqu'un répertoire contient des millions de fichiers, l'entrée de répertoire elle-même devient très grande. L'entrée du répertoire doit être analysée pour chaque opération de suppression, ce qui prend différentes durées pour chaque fichier, en fonction de l'emplacement de l'entrée. Malheureusement, même après la suppression de tous les fichiers, l’entrée du répertoire conserve sa taille. Par conséquent, les opérations supplémentaires nécessitant l'analyse de l'entrée du répertoire prendront encore beaucoup de temps, même si le répertoire est maintenant vide. Le seul moyen de résoudre ce problème consiste à renommer le répertoire, à en créer un nouveau avec l'ancien nom et à transférer tous les fichiers restants dans le nouveau. Supprimez ensuite celui qui a été renommé.
la source
Je ne l'ai pas évalué, mais ce type l'a fait :
la source
find n'a tout simplement pas fonctionné pour moi, même après avoir modifié les paramètres de ext3 fs comme suggéré par les utilisateurs ci-dessus. Consommé beaucoup trop de mémoire. Ce script PHP a fait l'affaire - rapide, utilisation du processeur insignifiante, utilisation de la mémoire insignifiante:
J'ai posté un rapport de bogue concernant ce problème avec find: http://savannah.gnu.org/bugs/?31961
la source
J'ai récemment fait face à un problème similaire et j'étais incapable d'obtenir la
data=writeback
suggestion de ring0 de fonctionner (probablement parce que les fichiers se trouvent sur ma partition principale). En cherchant des solutions de contournement, je suis tombé sur ceci:Cela désactive complètement la journalisation, quelle que soit l'
data
option donnée àmount
. J'ai combiné cela avecnoatime
et le volume étaitdir_index
réglé, et cela semblait bien fonctionner. La suppression s’est en fait terminée sans que je n’aie besoin de la supprimer, mon système est resté réactif et il est maintenant remis en service (avec journalisation réactivée) sans aucun problème.la source
Assurez-vous de le faire:
ce qui devrait aussi accélérer les choses.
la source
La commande est très lente. Essayer:
la source
strace -r -p <pid of rm>
de vous connecter au processus rm en cours d'exécution. Ensuite, vous pouvez voir à quelle vitesseunlink
défilent les appels système. (-r
place le temps écoulé depuis l'appel système précédent au début de chaque ligne.)Est
dir_index
défini pour le système de fichiers? (tune2fs -l | grep dir_index
) Sinon, activez-le. C'est habituellement pour le nouveau RHEL.la source
Il y a quelques années, j'ai trouvé un répertoire de 16 millions de fichiers XML dans le
/
système de fichiers. En raison de la criticité du serveur, nous avons utilisé la commande suivante qui a duré environ 30 heures :Il s’agissait d’un vieux disque dur à 7 200 tr / min et, malgré le goulot d’étranglement IO et les pics de processeur, l’ancien serveur Web poursuivait son service.
la source
Mon option préférée est l'approche newfs, déjà suggérée. Le problème fondamental est, encore une fois, comme cela a déjà été noté, le balayage linéaire pour gérer la suppression est problématique.
rm -rf
devrait être presque optimal pour un système de fichiers local (NFS serait différent). Mais avec des millions de fichiers, 36 octets par nom de fichier et 4 par inode (une estimation, ne vérifiant pas la valeur de ext3), cela représente 40 * millions, à conserver dans la RAM uniquement pour le répertoire.En un rien de temps, vous écrasez la mémoire cache de métadonnées du système de fichiers sous Linux, de sorte que les blocs d'une page du fichier de répertoire soient supprimés pendant que vous utilisez encore une autre partie. le fichier est supprimé. Le réglage des performances Linux n’est pas de mon ressort, mais / proc / sys / {vm, fs} / contient probablement quelque chose de pertinent.
Si vous pouvez vous permettre des temps d'arrêt, vous pouvez envisager d'activer la fonctionnalité dir_index. Il bascule l'index de répertoire de linéaire à quelque chose de beaucoup plus optimal pour la suppression dans les grands répertoires (arbres binaires hachés).
tune2fs -O dir_index ...
suivi dee2fsck -D
travaillerait. Cependant, bien que je sois confiant que cela aiderait avant qu'il y ait des problèmes, je ne sais pas comment la conversion (e2fsck avec le-D
) se comporte lorsqu'il s'agit d'un répertoire v.large existant. Sauvegardes + sucer-it-and-see.la source
/proc/sys/fs/vfs_cache_pressure
pourrait être la valeur à utiliser, mais je ne sais pas si le répertoire lui-même compte pour le cache de page (parce que c'est ce qu'il est) ou le cache d'inode (car, même s'il n'est pas un inode, ce sont des métadonnées FS et regroupées pour cette raison). Comme je l'ai dit, le réglage de la machine virtuelle Linux n'est pas mon domaine. Jouez et voyez ce qui aide.Évidemment pas des pommes aux pommes ici, mais j'ai installé un petit test et fait ce qui suit:
Création de 100 000 fichiers de 512 octets dans un répertoire (
dd
et/dev/urandom
dans une boucle); oublié de chronométrer, mais il a fallu environ 15 minutes pour créer ces fichiers.A exécuté ce qui suit pour supprimer lesdits fichiers:
ls -1 | wc -l && time find . -type f -delete
Il s’agit d’un boîtier Pentium 4 à 2,8 GHz (quelques centaines de Go IDE 7200 tr / min je pense; EXT3). Noyau 2.6.27.
la source
rm
lenteur est horriblement lente pour un grand nombre de fichiers, d'où l'find -delete
option. Avec un caractère générique sur le shell, il développera chaque nom de fichier recherché, et je suppose qu'il existe une mémoire tampon limitée pour cela, vous pouvez donc voir à quel point cela pourrait devenir inefficace.Parfois, Perl peut faire des merveilles dans des cas comme celui-ci. Avez-vous déjà essayé si un petit script comme celui-ci pouvait surpasser bash et les commandes de base du shell?
Ou une autre approche, peut-être même plus rapide, de Perl:
MODIFIER: Je viens d'essayer mes scripts Perl. Le plus verbeux fait quelque chose de bien. Dans mon cas, j'ai essayé avec un serveur virtuel avec 256 Mo de RAM et un demi-million de fichiers.
time find /test/directory | xargs rm
résultats:par rapport à
la source
*(oN)
]De ce que je me souviens de la suppression des inodes dans les systèmes de fichiers ext est O (n ^ 2), donc plus vous supprimez de fichiers, plus vite le reste ira.
Une fois, j’ai été confronté à un problème similaire (bien que mes estimations aient pris environ 7 heures), c’est finalement le chemin suggéré par jftuga dans le premier commentaire .
la source
Eh bien, ce n'est pas une vraie réponse, mais ...
Serait-il possible de convertir le système de fichiers en ext4 et de voir si les choses changent?
la source
D'accord, cela a été couvert de différentes manières dans le reste du fil, mais je pensais que je mettrais mes deux sous. Le coupable de performance dans votre cas est probablement readdir. Vous récupérez une liste de fichiers qui ne sont pas nécessairement séquentiels sur le disque, ce qui entraîne un accès au disque partout lorsque vous supprimez le lien. Les fichiers sont suffisamment petits pour que l'opération de dissociation ne saute probablement pas trop au zéro. Si vous readdir puis triez par inode croissant, vous obtiendrez probablement de meilleures performances. Donc readdir dans la RAM (trier par inode) -> unlink -> profit.
Inode est une approximation approximative ici, mais je pense .. mais sur la base de votre cas d'utilisation, il pourrait être assez précis ...
la source
J'aurais probablement sorti un compilateur C et fait l'équivalent moral de votre script. Autrement dit, utilisez
opendir(3)
pour obtenir un descripteur de répertoire, puis utilisezreaddir(3)
pour obtenir le nom des fichiers, puis compilez les fichiers au fur et à mesure que je les dissocie et imprime de temps en temps "% d fichiers supprimés" (et éventuellement le temps écoulé ou l'horodatage en cours).Je ne m'attends pas à ce qu'il soit sensiblement plus rapide que la version du script shell, c'est simplement que je suis habitué à extraire le compilateur de temps en temps, soit parce qu'il n'y a pas de moyen propre de faire ce que je veux dans le shell ou parce que bien que réalisable en shell, il est lent de façon improductive.
la source
Vous êtes probablement confronté à des problèmes de réécriture du répertoire. Essayez de supprimer les fichiers les plus récents en premier. Examinez les options de montage qui différeront l'écriture sur le disque.
Pour une barre de progression, essayez quelque chose comme:
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
la source
Voici comment je supprime les millions de fichiers de trace qui peuvent parfois se rassembler sur un grand serveur de base de données Oracle:
Je constate que cela entraîne une suppression assez lente qui a un impact faible sur les performances du serveur, généralement une heure par million de fichiers sur une configuration "typique" de 10 000 IOPS.
Plusieurs minutes sont nécessaires avant l'analyse des répertoires, la génération de la liste de fichiers initiale et la suppression du premier fichier. À partir de là, a. est répété pour chaque fichier supprimé.
Le retard causé par l'écho au terminal s'est avéré suffisamment retard pour empêcher toute charge significative pendant la suppression.
la source
find /u* -maxdepth 3 -mindepth 3 -type d -path '*/app/*' -name diag -print0 | xargs -0I = find = -mindepth 4 -maxdepth 4 -type d -name 'trace' -print0 | xargs -0I = find = -mindepth 1 -maxdepth 1 -name '*.tr'
:? Ajoutez-delete
au dernier pour supprimer réellement des choses; tel qu'il est écrit, il ne fait que répertorier ce qu'il supprimerait. Notez que ceci est optimisé pour des situations où vous avez beaucoup de choses sans intérêt dans les annuaires voisins; si ce n'est pas le cas, vous pouvez grandement simplifier la logique.rm
se produise), ce qui vous permet d’avoir une entrée / sortie relativement efficace au démarrage, suivie de s douloureuses et hors d’ordrerm
. cela ne cause probablement pas beaucoup d'E / S, mais implique descandir
parcourir le répertoire à plusieurs reprises (sans provoquer d'E / S car il a déjà été chargé dans le cache de blocs; voir aussivfs_cache_pressure
). Si vous souhaitez ralentir les choses,ionice
c'est une option, mais j'utiliserais probablement une fraction de secondesleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
exécuterait unrm
par répertoire, de sorte que vous auriez moins de surcharge du processeur. Tant que vous disposez de beaucoup de temps processeur, réglez les entrées / sorties disque en forçant tout unrm
processus pour chaqueunlink
travail, je suppose, mais c'est moche. perl avec un sommeil par liaison serait plus agréable si dormir entrerm
des annuaires entiers à la fois est trop volumineux. (-execdir sh -c ...
peut - être)Vous pouvez utiliser les fonctionnalités de parallélisation 'xargs':
la source
la source
en fait, celui-ci est un peu meilleur si le shell que vous utilisez ne développe pas en ligne de commande:
la source