So löschen Sie Millionen von Dateien, ohne den Server zu stören

11

Ich möchte ein Nginx-Cache-Verzeichnis löschen, das ich schnell gelöscht habe durch:

mv cache cache.bak
mkdir cache
service nginx restart

Jetzt habe ich einen cache.bakOrdner mit 2 Millionen Dateien. Ich möchte es löschen, ohne den Server zu stören.

Ein einfacher rm -rf cache.bakPapierkorb auf dem Server, selbst die einfachste HTTP-Antwort dauert 16 Sekunden, während rm ausgeführt wird, daher kann ich das nicht tun.

Ich habe es versucht ionice -c3 rm -rf cache.bak, aber es hat nicht geholfen. Der Server verfügt über eine Festplatte, nicht über eine SSD. Wahrscheinlich ist dies auf einer SSD kein Problem.

Ich glaube, die beste Lösung wäre eine Art Drosselung, wie es der in Nginx integrierte Cache-Manager tut.

Wie würden Sie das lösen? Gibt es ein Tool, das genau das kann?

ext4 unter Ubuntu 16.04

Hyperknoten
quelle
1
Wie haben Sie sich von "rm -rf cache.bak" erholt? Es scheint, dass nginx beim Umbenennen ausgeführt wurde, sodass möglicherweise Dateideskriptoren beibehalten und sogar in das neue Verzeichnis gewechselt wurden. Ich denke, Sie müssen Nginx vollständig herunterfahren, den Cache löschen und dann neu starten.
Jan Steinman
6
Bitte stecken Sie Ihren Cache in Zukunft in ein separates Dateisystem. Auf diese Weise können Sie einfach das Dateisystem zerstören, was viel schneller ist als der Versuch, Millionen von Dateien zu löschen. Das habe ich vor einigen Jahren auf die harte Tour mit einem Hylafax-Spool-Verzeichnis gelernt, das zig Millionen Dateien enthält.
Dennis Kaarsemaker
Haben Sie versucht, rmmit nice zu laufen ?
Vladislav Rastrusny
Versuchen Sie rsync, um schnell zu löschen - Antworten auf einen ähnlichen Fall - unix.stackexchange.com/questions/37329/…
kawu
Vielen Dank für alle Kommentare. Ich habe meine Ergebnisse zusammengefasst, um eine Antwort zu schreiben.
Hyperknot

Antworten:

9

Erstellen Sie ein Bash-Skript wie folgt:

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

Speichern Sie es deleter.shzum Beispiel mit Namen . Führen Sie es aus chmod u+x deleter.sh, um es ausführbar zu machen.

Dieses Skript löscht alle als Argumente übergebenen Dateien und schläft dann 0,5 Sekunden.

Dann kannst du rennen

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

Dieser Befehl ruft eine Liste aller Dateien in cache.bak ab und übergibt die fünf Dateinamen gleichzeitig an das Löschskript.

Sie können also einstellen, wie viele Dateien gleichzeitig gelöscht werden und wie lange zwischen den einzelnen Löschvorgängen eine Verzögerung liegt.

Tero Kilkanen
quelle
Vielen Dank für diese Lösung, ich habe sie in meine allgemeine Beschreibung aufgenommen. Eine Frage, wie geht das mit großen ns um? Normalerweise hatte ich Probleme mit * Zeichen in großen Verzeichnissen, die Fehler verursachten. Ist das hier nicht der Fall?
Hyperknot
xargsversteht die maximale Größe einer Befehlszeile und versucht, diese standardmäßig nicht zu überschreiten. Dieser hat zusätzliche Grenzen von nicht mehr als 5 Pfaden gleichzeitig.
BowlOfRed
1
Beachten Sie jedoch, dass bei einer Geschwindigkeit von 10 Dateien pro Sekunde das Löschen von 2 Millionen Dateien 55 Stunden dauert.
Andrew Henle
4

Sie sollten in Betracht ziehen, Ihren Cache in einem separaten Dateisystem zu speichern, das Sie wie in den Kommentaren angegeben ein- und aushängen können. Bis dahin können Sie diesen einen Liner verwenden, /usr/bin/find /path/to/files/ -type f -print0 -exec sleep 0.2 \; -exec echo \; -deletevorausgesetzt, Ihre Find-Binärdatei befindet sich unter / usr / bin und Sie möchten den Fortschritt auf dem Bildschirm sehen. Passen Sie den Schlaf entsprechend an, damit Ihre Festplatte nicht überlastet wird.

Alex
quelle
Man braucht -print0hier nicht, da Sie die Ausgabe von findnirgendwo leiten.
Tero Kilkanen
Vielleicht interessiert Sie auch, was gerade läuft. Nennen wir es Paranoia, aber ich möchte immer sicher sein, dass ich die richtigen Dateien lösche.
Alex
Ah wahr, ich habe den Befehl nicht richtig dekodiert, mein schlechtes.
Tero Kilkanen
3

Möglicherweise möchten Sie ionice in einem Skript ausprobieren, das die Ausgabe eines Suchbefehls verbraucht. So etwas wie das Folgende:

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
)

Abhängig vom Dateisystem kann jedes Löschen einer Datei dazu führen, dass das gesamte Verzeichnis neu geschrieben wird. Für große Verzeichnisse kann das ein ziemlicher Erfolg sein. Für die Inode-Tabelle sind möglicherweise zusätzliche Aktualisierungen und möglicherweise eine Liste mit freiem Speicherplatz erforderlich.

Wenn das Dateisystem über ein Journal verfügt, werden Änderungen in das Journal geschrieben. angewendet; und aus dem Tagebuch entfernt. Dies erhöht die E / A-Anforderungen für schreibintensive Aktivitäten.

Möglicherweise möchten Sie ein Dateisystem ohne Journal für den Cache verwenden.

Anstelle von ionice können Sie einen Schlafbefehl verwenden, um die Aktionen zu begrenzen. Dies funktioniert auch dann, wenn ionice dies nicht tut, aber es wird lange dauern, bis alle Ihre Dateien gelöscht sind.

BillThor
quelle
2

Ich habe hier viele nützliche Antworten / Kommentare erhalten, die ich abschließen und auch meine Lösung zeigen möchte.

  1. Ja, der beste Weg, dies zu verhindern , besteht darin, das Cache-Verzeichnis in einem separaten Dateisystem zu speichern. Das Nuking / schnelle Formatieren eines Dateisystems dauert immer höchstens einige Sekunden (möglicherweise Minuten), unabhängig davon, wie viele Dateien / Verzeichnisse darauf vorhanden waren.

  2. Die ionice/ nice-Lösungen haben nichts getan, da der Löschvorgang tatsächlich fast keine E / A verursachte. Was die E / A verursacht hat, war meiner Meinung nach, dass sich Warteschlangen / Puffer auf Kernel- / Dateisystemebene füllen, wenn Dateien durch den Löschvorgang zu schnell gelöscht wurden.

  3. Die Art und Weise, wie ich es gelöst habe, ähnelt der Lösung von Tero Kilkanen, erfordert jedoch nicht das Aufrufen eines Shell-Skripts. Ich habe den eingebauten --bwlimitSchalter von rsync verwendet , um die Geschwindigkeit des Löschens zu begrenzen.

Voller Befehl war:

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

Jetzt gibt bwlimit die Bandbreite in Kilobyes an, die in diesem Fall auf den Dateinamen oder den Pfad der Dateien angewendet wird. Durch Einstellen auf 1 KBit / s wurden ungefähr 100.000 Dateien pro Stunde oder 27 Dateien pro Sekunde gelöscht. Dateien hatten relative Pfade wie cache.bak/e/c1/db98339573acc5c76bdac4a601f9ec1e, was 47 Zeichen lang ist, also würde es 1000/47 ~ = 21 Dateien pro Sekunde geben, so ähnlich wie meine Schätzung von 100.000 Dateien pro Stunde.

Warum jetzt --bwlimit=1? Ich habe verschiedene Werte ausprobiert:

  • 10000, 1000, 100 -> System verlangsamt sich wie zuvor
  • 10 -> System funktioniert eine Weile recht gut, erzeugt aber etwa einmal pro Minute teilweise Verlangsamungen. HTTP-Antwortzeiten noch <1 Sek.
  • 1 -> überhaupt keine Systemverlangsamung. Ich habe es nicht eilig und 2 Millionen Dateien können auf <1 Tag auf diese Weise gelöscht werden, also wähle ich es.

Ich mag die Einfachheit der eingebauten Methode von rsync, aber diese Lösung hängt von der relativen Pfadlänge ab. Kein großes Problem, da die meisten Menschen durch Ausprobieren den richtigen Wert finden würden.

Hyperknoten
quelle
Und jetzt bin ich gespannt, was der Disketteneffekt wäre, wenn Sie so etwas wie "mv cache.dir-old / dev / null" machen würden
ivanivan