Hintergrund: Physischer Server, ungefähr zwei Jahre alt, SATA-Laufwerke mit 7200 U / min, die an eine 3Ware-RAID-Karte angeschlossen sind, ext3 FS-Mounted Noatime und Daten = bestellt, nicht unter verrückter Last, Kernel 2.6.18-92.1.22.el5, Betriebszeit 545 Tage . Directory enthält keine Unterverzeichnisse, nur Millionen kleiner (~ 100 Byte) Dateien, einige größere (ein paar KB).
Wir haben einen Server, der in den letzten Monaten ein bisschen kuckuck gegangen ist, aber wir haben ihn erst neulich bemerkt, als er anfing, nicht in ein Verzeichnis schreiben zu können, weil er zu viele Dateien enthielt. Insbesondere hat es begonnen, diesen Fehler in / var / log / messages auszulösen:
ext3_dx_add_entry: Directory index full!
Auf der fraglichen Festplatte sind noch viele Inodes vorhanden:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Ich schätze, das heißt, wir haben das Limit erreicht, wie viele Einträge in der Verzeichnisdatei selbst sein können. Keine Ahnung, wie viele Dateien das sein würden, aber wie Sie sehen können, können es nicht mehr als drei Millionen sein. Nicht dass das gut wäre, wohlgemerkt! Aber das ist Teil einer meiner Fragen: Was genau ist diese Obergrenze? Ist es einstellbar? Bevor ich angeschrien werde - ich möchte es leiser stellen ; Dieses riesige Verzeichnis verursachte alle möglichen Probleme.
Wie auch immer, wir haben das Problem in dem Code aufgespürt, der all diese Dateien generiert hat, und wir haben es korrigiert. Jetzt stecke ich beim Löschen des Verzeichnisses fest.
Ein paar Möglichkeiten hier:
rm -rf (dir)
Ich habe es zuerst versucht. Ich gab es auf und tötete es, nachdem es anderthalb Tage ohne erkennbare Auswirkungen gelaufen war.
- Unlink (2) im Verzeichnis: Auf jeden Fall eine Überlegung wert, aber die Frage ist, ob es schneller wäre, die Dateien im Verzeichnis über fsck zu löschen, als über Unlink (2). Das heißt, auf die eine oder andere Weise muss ich diese Inodes als unbenutzt markieren. Dies setzt natürlich voraus, dass ich fsck anweisen kann, keine Einträge in den Dateien in / lost + found abzulegen. ansonsten habe ich gerade mein problem verschoben. Neben all den anderen Bedenken muss ich wahrscheinlich einige interne FS-Funktionen aufrufen, nachdem ich etwas mehr darüber gelesen habe, da keine der unlink (2) -Varianten, die ich finden kann, es mir erlauben würde, nur munter zu löschen ein Verzeichnis mit Einträgen darin. Pooh.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
Dies ist eigentlich die gekürzte Version; Die eigentliche Version, die ich gerade ausführe und die nur einige Fortschrittsberichte und einen sauberen Stopp hinzufügt, wenn die zu löschenden Dateien ausgehen, ist:
export i = 0; time (while [wahr]; do ls -Uf | head -n 3 | grep -qF '.png' || brechen; ls -Uf | head -n 10000 | xargs rm -f 2> / dev / null; exportiere i = $ (($ i + 10000)); echo "$ i ..."; getan )
Dies scheint ziemlich gut zu funktionieren. Während ich das schreibe, hat es in den letzten 30 Minuten ungefähr 260.000 Dateien gelöscht.
- Ist, wie oben erwähnt, das Limit für Verzeichniseinträge einstellbar?
- Warum hat es "echte 7m9.561s / Benutzer 0m0.001s / sys 0m0.001s" gedauert, um eine einzelne Datei zu löschen, die die erste in der Liste war, die von zurückgegeben wurde
ls -U
, und es hat vielleicht zehn Minuten gedauert , um die ersten 10.000 Einträge mit der zu löschen? Kommando in # 3, aber jetzt schleppt es sich ziemlich glücklich voran? Im Übrigen wurden 260.000 in ungefähr 30 Minuten gelöscht, aber es dauerte jetzt weitere 15 Minuten, um weitere 60.000 zu löschen. Warum die großen Geschwindigkeitsschwankungen? - Gibt es einen besseren Weg, um so etwas zu tun? Speichern Sie nicht Millionen von Dateien in einem Verzeichnis. Ich weiß, dass das albern ist, und es wäre auf meiner Uhr nicht passiert. Das Problem zu googeln und durch SF und SO zu schauen, bietet eine Menge Variationen
find
, die aus mehreren offensichtlichen Gründen nicht wesentlich schneller sein werden als mein Ansatz. Aber hat die Delete-via-Fsck-Idee irgendwelche Beine? Oder etwas ganz anderes? Ich bin gespannt darauf, über den Tellerrand (oder innerhalb der nicht bekannten Box) zu denken.
Letzte Skriptausgabe !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Also drei Millionen Dateien in etwas mehr als vier Stunden gelöscht.
rm -rfv | pv -l >/dev/null
. pv sollte im EPEL- Repository verfügbar sein .Antworten:
Die
data=writeback
Mount-Option sollte ausprobiert werden, um ein Journaling des Dateisystems zu verhindern. Dies sollte nur während der Löschzeit erfolgen. Es besteht jedoch das Risiko, dass der Server während des Löschvorgangs heruntergefahren oder neu gestartet wird.Nach dieser Seite ,
Die Option wird entweder in
fstab
oder während des Montagebetriebes, ersetztdata=ordered
mitdata=writeback
. Das Dateisystem mit den zu löschenden Dateien muss neu gemountet werden.quelle
commit
Option verlängern : "Dieser Standardwert (oder ein beliebiger niedriger Wert) beeinträchtigt die Leistung, ist aber gut für die Datensicherheit. Das Setzen auf 0 hat den gleichen Effekt wie das Belassen des Standardwerts (5 Sekunden) ). Wenn Sie einen sehr großen Wert einstellen, wird die Leistung verbessert ".data=writeback
Die Metadaten der Journale bleiben erhalten, bevor sie in das Hauptdateisystem geschrieben werden. So wie ich es verstehe, erzwingt es einfach keine Reihenfolge zwischen Dingen wie dem Schreiben einer Extent-Map und dem Schreiben von Daten in diese Extents. Vielleicht gibt es noch andere Ordnungsbeschränkungen, die es auch lockert, wenn Sie einen perfekten Gewinn daraus ziehen. Selbstverständlich könnte die Montage ohne den Zapfen noch leistungsfähiger sein. (Es kann vorkommen, dass die Metadatenänderungen nur im RAM vorgenommen werden, ohne dass vor Abschluss des Unlink-Vorgangs etwas auf der Festplatte gespeichert werden muss.)Während eine Hauptursache für dieses Problem die Leistung von ext3 mit Millionen von Dateien ist, ist die eigentliche Hauptursache für dieses Problem anders.
Wenn ein Verzeichnis aufgelistet werden muss, wird readdir () für das Verzeichnis aufgerufen, das eine Liste von Dateien liefert. readdir ist ein posix-Aufruf, aber der echte Linux-Systemaufruf, der hier verwendet wird, heißt 'getdents'. Getdents listet Verzeichniseinträge auf, indem ein Puffer mit Einträgen gefüllt wird.
Das Problem ist hauptsächlich darauf zurückzuführen, dass readdir () eine feste Puffergröße von 32 KB verwendet, um Dateien abzurufen. Wenn ein Verzeichnis größer und größer wird (die Größe nimmt zu, wenn Dateien hinzugefügt werden), wird ext3 langsamer und langsamer, um Einträge abzurufen, und die zusätzliche 32-KB-Puffergröße von readdir reicht nur aus, um einen Bruchteil der Einträge in das Verzeichnis aufzunehmen. Dies führt dazu, dass readdir immer wieder eine Schleife durchläuft und den teuren Systemaufruf immer wieder aufruft.
Zum Beispiel zeigt "ls -1 | wc-l" in einem Testverzeichnis, das ich mit mehr als 2,6 Millionen Dateien erstellt habe, eine große Ausgabe vieler getdent-Systemaufrufe.
Außerdem war die in diesem Verzeichnis verbrachte Zeit erheblich.
Die Methode, um dies effizienter zu gestalten, besteht darin, getdents manuell mit einem viel größeren Puffer aufzurufen. Dies verbessert die Leistung erheblich.
Jetzt sollten Sie getdents nicht selbst manuell aufrufen, daher gibt es keine Schnittstelle für die normale Verwendung (sehen Sie in der Manpage nach, ob getdents angezeigt werden!). Sie können es jedoch manuell aufrufen und den Systemaufruf effizienter gestalten.
Dadurch wird die Zeit zum Abrufen dieser Dateien drastisch reduziert. Ich habe ein Programm geschrieben, das dies tut.
Dies bekämpft zwar nicht das zugrunde liegende Grundproblem (viele Dateien in einem Dateisystem, das eine schlechte Leistung erbringt). Es ist wahrscheinlich viel, viel schneller als viele der Alternativen, die veröffentlicht werden.
Aus Vorsichtsgründen sollte man das betroffene Verzeichnis entfernen und es anschließend neu erstellen. Verzeichnisse nehmen immer nur an Größe zu und können aufgrund der Größe des Verzeichnisses auch mit wenigen darin enthaltenen Dateien eine schlechte Leistung aufweisen.
Edit: Ich habe das ziemlich aufgeräumt. Es wurde eine Option hinzugefügt, die es Ihnen ermöglicht, zur Laufzeit auf der Befehlszeile zu löschen, und ein paar Baumstamm-Elemente entfernt, die, ehrlich gesagt, im besten Fall fragwürdig waren. Auch wurde gezeigt, Speicherbeschädigung zu produzieren.
Sie können jetzt tun
dentls --delete /my/path
Neue Ergebnisse. Basierend auf einem Verzeichnis mit 1,82 Millionen Dateien.
War irgendwie überrascht, dass das immer noch so gut funktioniert!
quelle
[256]
sollte wahrscheinlich sein[FILENAME_MAX]
, und zwei, mein Linux (2.6.18 == CentOS 5.x) scheint keinen d_type-Eintrag in dirent zu enthalten (zumindest laut getdents (2)).Ist es möglich, alle anderen Dateien aus diesem Dateisystem in einem temporären Speicherort zu sichern, die Partition neu zu formatieren und die Dateien dann wiederherzustellen?
quelle
In ext3 gibt es keine Beschränkung pro Verzeichnisdatei, nur die Beschränkung des Dateisystem-Inodes (ich glaube, es gibt eine Beschränkung für die Anzahl der Unterverzeichnisse).
Möglicherweise haben Sie nach dem Entfernen der Dateien weiterhin Probleme.
Wenn ein Verzeichnis Millionen von Dateien enthält, wird der Verzeichniseintrag selbst sehr groß. Der Verzeichniseintrag muss nach jedem Entfernungsvorgang durchsucht werden. Dies dauert für jede Datei unterschiedlich lange, je nachdem, wo sich ihr Eintrag befindet. Leider behält der Verzeichniseintrag auch nach dem Entfernen aller Dateien seine Größe. Weitere Vorgänge, die das Durchsuchen des Verzeichniseintrags erfordern, dauern daher auch dann noch lange, wenn das Verzeichnis jetzt leer ist. Die einzige Möglichkeit, dieses Problem zu lösen, besteht darin, das Verzeichnis umzubenennen, ein neues mit dem alten Namen zu erstellen und alle verbleibenden Dateien auf das neue zu übertragen. Dann löschen Sie die umbenannte.
quelle
Ich habe es nicht verglichen, aber dieser Typ hat Folgendes getan :
quelle
find hat bei mir einfach nicht funktioniert, auch nach dem ändern der ext3 fs parameter wie von den benutzern oben vorgeschlagen. Verbraucht viel zu viel Speicher. Dieses PHP-Skript hat den Trick getan - schnelle, unbedeutende CPU-Auslastung, unbedeutende Speicherauslastung:
Ich habe einen Fehlerbericht zu diesem Problem mit find gepostet: http://savannah.gnu.org/bugs/?31961
quelle
Ich hatte kürzlich ein ähnliches Problem und konnte den
data=writeback
Vorschlag von ring0 nicht zum Laufen bringen (möglicherweise aufgrund der Tatsache, dass sich die Dateien auf meiner Hauptpartition befinden). Bei der Suche nach Problemumgehungen bin ich auf Folgendes gestoßen:Dadurch wird die Aufzeichnung vollständig deaktiviert, unabhängig von der
data
Option, die Sie aktivierenmount
. Ich kombinierte dies mitnoatime
und die Lautstärke wardir_index
eingestellt, und es schien ziemlich gut zu funktionieren. Der Löschvorgang wurde tatsächlich abgeschlossen, ohne dass ich ihn beenden musste. Mein System reagierte weiterhin und ist jetzt ohne Probleme wieder einsatzbereit (mit aktivierter Journalfunktion).quelle
Stellen Sie sicher, dass Sie:
das sollte die Sache auch ein bisschen beschleunigen.
quelle
Es ist ein sehr langsamer Befehl. Versuchen:
quelle
strace -r -p <pid of rm>
, um eine Verbindung zum bereits ausgeführten rm-Prozess herzustellen. Dann können Sie sehen, wie schnellunlink
Systemaufrufe vorbeirollen. (-r
Stellt die Zeit seit dem vorherigen Systemaufruf am Anfang jeder Zeile ein.)Ist
dir_index
für das Dateisystem eingestellt? (tune2fs -l | grep dir_index
) Wenn nicht, aktivieren Sie es. Normalerweise ist es für neue RHEL aktiviert.quelle
Vor ein paar Jahren habe ich ein Verzeichnis mit 16 Millionen XML- Dateien im
/
Dateisystem gefunden. Aufgrund der Kritik des Servers haben wir den folgenden Befehl verwendet, der ungefähr 30 Stunden dauerte :Es war eine alte Festplatte mit 7200 U / min , und trotz des E / A-Engpasses und der CPU-Spitzen setzte der alte Webserver seinen Dienst fort.
quelle
Meine bevorzugte Option ist der bereits vorgeschlagene newfs-Ansatz. Das Grundproblem ist wiederum, wie bereits erwähnt, die lineare Abtastung, um das Löschen zu handhaben, problematisch.
rm -rf
sollte für ein lokales Dateisystem nahezu optimal sein (NFS wäre anders). Aber bei Millionen von Dateien, 36 Bytes pro Dateiname und 4 Bytes pro Inode (eine Schätzung, die den Wert für ext3 nicht überprüft), sind das 40 * Millionen, die nur für das Verzeichnis im RAM gespeichert werden müssen.Vermutlich zerschlagen Sie den Metadaten-Cache des Dateisystems in Linux, sodass Blöcke für eine Seite der Verzeichnisdatei gelöscht werden, während Sie noch einen anderen Teil verwenden, um diese Seite des Caches beim nächsten Mal erneut aufzurufen Datei wird gelöscht. Linux Performance Tuning ist nicht mein Bereich, aber / proc / sys / {vm, fs} / enthält wahrscheinlich etwas Relevantes.
Wenn Sie sich Ausfallzeiten leisten können, können Sie die Funktion dir_index aktivieren. Es schaltet den Verzeichnisindex von linear auf etwas weitaus Optimaleres zum Löschen in großen Verzeichnissen (gehashte B-Bäume).
tune2fs -O dir_index ...
gefolgt vone2fsck -D
würde funktionieren. Obwohl ich zuversichtlich bin, dass dies helfen würde, bevor es Probleme gibt, weiß ich nicht, wie die Konvertierung (e2fsck mit dem-D
) ausgeführt wird, wenn ein vorhandenes v.large-Verzeichnis verwendet wird. Backups + Wahnsinn.quelle
/proc/sys/fs/vfs_cache_pressure
dies der zu verwendende Wert sein könnte, aber ich weiß nicht, ob das Verzeichnis selbst für den Seiten-Cache (weil es so ist) oder für den Inode-Cache (weil es kein Inode ist) gilt Inode sind es FS-Metadaten, die aus diesem Grund dort gebündelt sind. Wie gesagt, Linux VM Tuning ist nicht mein Bereich. Spielen und sehen, was hilft.Natürlich keine Äpfel zu Äpfeln hier, aber ich habe einen kleinen Test gemacht und folgendes gemacht:
Erstellt 100.000 512-Byte-Dateien in einem Verzeichnis (
dd
und/dev/urandom
in einer Schleife). Ich habe die Zeit vergessen, aber es dauerte ungefähr 15 Minuten, um diese Dateien zu erstellen.Führen Sie die folgenden Schritte aus, um diese Dateien zu löschen:
ls -1 | wc -l && time find . -type f -delete
Dies ist eine Pentium 4-Box mit 2,8 GHz (einige hundert GB IDE mit 7200 U / min, glaube ich; EXT3). Kernel 2.6.27.
quelle
rm
das bei einer großen Anzahl von Dateien schrecklich langsam ist, daher diefind -delete
Option. Mit einem Platzhalter in der Shell wird jeder übereinstimmende Dateiname erweitert, und ich gehe davon aus, dass es dafür einen begrenzten Speicherpuffer gibt, sodass Sie sehen können, wie ineffizient das werden würde.Manchmal kann Perl in solchen Fällen Wunder wirken. Haben Sie bereits versucht, ob ein kleines Skript wie dieses Bash und die grundlegenden Shell-Befehle übertreffen könnte?
Oder ein anderer, vielleicht sogar noch schnellerer Perl-Ansatz:
EDIT: Ich habe gerade meine Perl-Skripte ausprobiert. Je ausführlicher man etwas richtig macht. In meinem Fall habe ich dies mit einem virtuellen Server mit 256 MB RAM und einer halben Million Dateien versucht.
time find /test/directory | xargs rm
Ergebnisse:verglichen mit
quelle
*(oN)
]Soweit ich mich erinnere, ist das Löschen von Inodes in ext-Dateisystemen O (n ^ 2). Je mehr Dateien Sie löschen, desto schneller wird der Rest ausgeführt.
Es gab ein einziges Mal, dass ich mit einem ähnlichen Problem konfrontiert war (obwohl meine Schätzungen eine Löschzeit von ~ 7 Stunden betrachteten), und am Ende ging die von JFTUGA vorgeschlagene Route im ersten Kommentar .
quelle
Nun, das ist keine richtige Antwort, aber ...
Wäre es möglich, das Dateisystem nach ext4 zu konvertieren und zu sehen, ob sich die Dinge ändern?
quelle
Okay, das wurde im Rest des Threads auf verschiedene Arten abgedeckt, aber ich dachte, ich würde meine zwei Cent einwerfen. Der Leistungsschuldige in Ihrem Fall ist wahrscheinlich readdir. Sie erhalten eine Liste von Dateien zurück, die auf der Festplatte nicht unbedingt in irgendeiner Weise sequentiell sind, wodurch beim Trennen der Verknüpfung überall auf die Festplatte zugegriffen wird. Die Dateien sind so klein, dass der Vorgang zum Aufheben der Verknüpfung wahrscheinlich nicht zu weit aus dem Speicherplatz herausspringt. Wenn Sie readdir lesen und dann nach aufsteigendem Inode sortieren, erhalten Sie wahrscheinlich eine bessere Leistung. Also readdir in ram (sortiere nach inode) -> unlink -> profit.
Inode ist hier eine grobe Annäherung, denke ich .. aber basierend auf Ihrem Anwendungsfall könnte es ziemlich genau sein ...
quelle
Ich hätte wahrscheinlich einen C-Compiler herausgepeitscht und das moralische Äquivalent Ihres Skripts erstellt. Das heißt, verwenden Sie
opendir(3)
, um ein Verzeichnis-Handle abzurufen, und dannreaddir(3)
, um den Namen der Dateien abzurufen. Führen Sie dann beim Aufheben der Verknüpfung eine Zusammenfassung der Dateien durch und drucken Sie ab und zu "% d Dateien gelöscht" (und möglicherweise die abgelaufene Zeit oder den aktuellen Zeitstempel).Ich erwarte nicht, dass es merklich schneller ist als die Shell-Skript-Version. Ich bin es nur gewohnt, hin und wieder den Compiler rauszureißen, entweder weil es keine saubere Möglichkeit gibt, das zu tun, was ich von der Shell will, oder weil Obwohl es in der Shell machbar ist, ist es auf diese Weise unproduktiv langsam.
quelle
Wahrscheinlich treten beim Umschreiben des Verzeichnisses Probleme auf. Versuchen Sie zuerst, die neuesten Dateien zu löschen. Sehen Sie sich die Mount-Optionen an, mit denen das Zurückschreiben auf die Festplatte verzögert wird.
Versuchen Sie für eine Fortschrittsanzeige etwas wie
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
quelle
So lösche ich die Millionen von Tracedateien, die manchmal auf einem großen Oracle-Datenbankserver gesammelt werden können:
Ich stelle fest, dass dies zu einer relativ langsamen Löschung führt, die sich nur geringfügig auf die Serverleistung auswirkt, normalerweise in der Größenordnung von einer Stunde pro Million Dateien bei einem "typischen" 10.000-IOPS-Setup.
Oft dauert es mehrere Minuten, bis die Verzeichnisse durchsucht, die erste Dateiliste erstellt und die erste Datei gelöscht wurde. Von da an und weiter, a. wird für jede gelöschte Datei zurückgemeldet.
Die Verzögerung, die durch das Echo des Terminals verursacht wird, hat sich als ausreichend verzögert erwiesen, um eine signifikante Belastung während des Löschvorgangs zu verhindern.
quelle
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'
:? In-delete
zum letzten tatsächlich Dinge löschen; Wie geschrieben, listet es nur auf, was es löschen würde. Beachten Sie, dass dies für Situationen optimiert ist, in denen Sie viele uninteressante Dinge in nahe gelegenen Verzeichnissen haben. Ist dies nicht der Fall, können Sie die Logik erheblich vereinfachen.rm
Fall ist ), so dass Sie sich von dem relativ effizientem I / O beim Start haben, gefolgt von schmerzhafter, out-of-orderrm
s Das verursacht wahrscheinlich nicht viel E / A, erfordert aberscandir
das wiederholte Durchlaufen des Verzeichnisses (ohne E / A zu verursachen, da dies bereits in den Blockcache geladen wurde; siehe auchvfs_cache_pressure
). Wenn Sie etwas verlangsamen möchten,ionice
ist dies eine Option, aber ich würde wahrscheinlich Sekundenbruchteile verwendensleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
würde einerm
pro Verzeichnis ausführen , damit Sie weniger CPU-Overhead haben. Solange Sie Tonnen an CPU-Zeit übrig haben, drosseln Sie die Festplatten-E / A, indem Sie einen ganzenrm
Prozess für jedeunlink
Arbeit forken, denke ich, aber es ist hässlich. perl mit einem sleep per unlink wäre schöner, wenn das schlafen zwischenrm
ganzen verzeichnissen zu platzen wäre. (-execdir sh -c ...
vielleicht)Sie könnten 'xargs'-Parallelisierungsfunktionen verwenden:
quelle
quelle
Tatsächlich ist dieses etwas besser, wenn die von Ihnen verwendete Shell eine Befehlszeilenerweiterung ausführt:
quelle