Millionen (kleiner) Textdateien in einem Ordner


15

Wir möchten Millionen von Textdateien in einem Linux-Dateisystem speichern, um eine beliebige Sammlung als Service komprimieren und bereitstellen zu können. Wir haben andere Lösungen ausprobiert, beispielsweise eine Schlüssel- / Wertedatenbank, aber unsere Anforderungen an Parallelität und Parallelität machen die Verwendung des nativen Dateisystems zur besten Wahl.

Am einfachsten ist es, alle Dateien in einem Ordner zu speichern:

$ ls text_files/
1.txt
2.txt
3.txt

die auf einem EXT4 Dateisystem möglich sein sollen , die keine Begrenzung der Anzahl der Dateien in einem Ordner.

Die beiden FS-Prozesse sind:

  1. Schreiben Sie eine Textdatei von Web Scrape (sollte nicht durch die Anzahl der Dateien im Ordner beeinflusst werden).
  2. Komprimieren Sie die ausgewählten Dateien anhand der Liste der Dateinamen.

Meine Frage ist, wird das Speichern von bis zu zehn Millionen Dateien in einem Ordner die Leistung der oben genannten Vorgänge oder die allgemeine Systemleistung anders beeinflussen als das Erstellen eines Unterordner-Baums für die Dateien, in denen sie gespeichert werden sollen?


4
Verwandte Themen : So beheben Sie zeitweise auftretende Fehler "Kein Platz mehr auf dem Gerät", wenn das Gerät über ausreichend Platz verfügt . Die Verwendung von dir_index, die häufig standardmäßig aktiviert ist, beschleunigt die Suche, kann jedoch die Anzahl der Dateien pro Verzeichnis begrenzen.
Mark Plotnick

Probieren Sie es einfach auf einer virtuellen Maschine aus und sehen Sie, wie es ist. Mit bash ist es einfach, einen Ordner mit einer Million Textdateien mit zufälligen Zeichen zu füllen. Ich habe das Gefühl, dass Sie auf diese Weise wirklich nützliche Informationen erhalten, zusätzlich zu dem, was Sie hier lernen werden.
JoshuaD

2
@JoshuaD: Wenn Sie alles auf einmal auf einem frischen FS bevölkern , sind Sie wahrscheinlich alle die Inodes zusammenhängenden auf der Festplatte haben, so ls -loder irgendetwas anderes , das statjeden Inode im Verzeichnis s (zB bashGlobbing / Tabulatorvervollständigung) wird künstlich schneller als nach einigem Verschleiß (einige Dateien löschen, einige neue schreiben). ext4 könnte hier besser abschneiden als XFS, da XFS dynamisch Speicherplatz für Inodes im Vergleich zu Daten zuweist, sodass die Inodes meines Erachtens verstreuter sind. (Aber das ist eine reine Vermutung, die auf sehr wenig detailliertem Wissen basiert. Ich habe ext4 kaum benutzt.) Gehen Sie mit abc/def/Subdirs.
Peter Cordes

Ja, ich glaube nicht, dass der Test, den ich vorgeschlagen habe, dem OP sagen kann, dass "das funktioniert", aber es könnte ihm definitiv schnell sagen, dass "das nicht funktioniert", was nützlich ist.
JoshuaD

1
Unsere Anforderungen an Parallelität und Parallelität machen die Verwendung des nativen Dateisystems jedoch zur besten Wahl. Was haben Sie versucht? Auf den ersten Blick würde sogar ein RDBMS der unteren Preisklasse wie MySQL und ein Java-Servlet, mit dem die Zip-Dateien imZipOutputStream Handumdrehen erstellt werden, so gut wie jedes kostenlose native Linux-Dateisystem schlagen - ich bezweifle, dass Sie für IBMs GPFS bezahlen möchten. Die Schleife, um eine JDBC-Ergebnismenge zu verarbeiten und diesen Zip-Stream zu erstellen, besteht wahrscheinlich nur aus 6-8 Zeilen Java-Code.
Andrew Henle

Antworten:


10

Der lsBefehl oder sogar die TAB-Vervollständigung oder die Platzhaltererweiterung durch die Shell zeigen ihre Ergebnisse normalerweise in alphanumerischer Reihenfolge an. Dazu muss die gesamte Verzeichnisliste gelesen und sortiert werden. Mit zehn Millionen Dateien in einem einzigen Verzeichnis nimmt dieser Sortiervorgang eine nicht zu vernachlässigende Zeit in Anspruch.

Wenn Sie dem Drang der TAB-Vervollständigung widerstehen können und beispielsweise die Namen der Dateien schreiben, die vollständig komprimiert werden sollen, sollte es keine Probleme geben.

Ein weiteres Problem mit Platzhaltern könnte die Platzhaltererweiterung sein, die möglicherweise mehr Dateinamen erzeugt, als auf eine Befehlszeile mit maximaler Länge passen. Die typische maximale Befehlszeilenlänge ist für die meisten Situationen mehr als ausreichend. Wenn es sich jedoch um Millionen von Dateien in einem einzelnen Verzeichnis handelt, ist dies keine sichere Annahme mehr. Wenn eine maximale Befehlszeilenlänge bei der Platzhaltererweiterung überschritten wird, schlagen die meisten Shells einfach die gesamte Befehlszeile fehl, ohne sie auszuführen.

Dies kann gelöst werden, indem Sie Ihre Platzhalteroperationen mit dem folgenden findBefehl ausführen :

find <directory> -name '<wildcard expression>' -exec <command> {} \+

oder eine ähnliche Syntax, wann immer dies möglich ist. Der find ... -exec ... \+berücksichtigt automatisch die maximale Befehlszeilenlänge und führt den Befehl so oft wie erforderlich aus, während die maximale Anzahl von Dateinamen an jede Befehlszeile angepasst wird.


Moderne Dateisysteme verwenden B, B + oder ähnliche Bäume, um Verzeichniseinträge zu speichern. en.wikipedia.org/wiki/HTree
dimm

4
Ja ... aber wenn die Shell oder der lsBefehl nicht erfahren, dass die Verzeichnisliste bereits sortiert ist, werden sie sich trotzdem die Zeit nehmen, um den Sortieralgorithmus auszuführen. Außerdem verwendet der Userspace möglicherweise eine lokalisierte Sortierreihenfolge (LC_COLLATE), die sich möglicherweise von der internen Vorgehensweise des Dateisystems unterscheidet.
TelcoM

17

Dies kommt einer meinungsbasierten Frage / Antwort gefährlich nahe, aber ich werde versuchen, einige Fakten mit meinen Meinungen zu versehen.

  1. Wenn Sie eine sehr große Anzahl von Dateien in einem Ordner haben, kann jede Shell-basierte Operation, die versucht, diese aufzuzählen (z. B. mv * /somewhere/else), den Platzhalter nicht erfolgreich erweitern, oder das Ergebnis ist möglicherweise zu groß für die Verwendung.
  2. ls Das Auflisten einer sehr großen Anzahl von Dateien dauert länger als das Auflisten einer kleinen Anzahl von Dateien.
  3. Das Dateisystem wird in der Lage sein, Millionen von Dateien in einem einzigen Verzeichnis zu verarbeiten, aber die Leute werden wahrscheinlich Schwierigkeiten haben.

Eine Empfehlung besteht darin, den Dateinamen in zwei, drei oder vier Zeichenblöcke aufzuteilen und diese als Unterverzeichnisse zu verwenden. Zum Beispiel somefilename.txtkönnte gespeichert werden als som/efi/somefilename.txt. Wenn Sie numerische Namen verwenden, teilen Sie diese von rechts nach links statt von links nach rechts, um eine gleichmäßigere Verteilung zu erzielen. Zum Beispiel 12345.txtkönnte als gespeichert werden 345/12/12345.txt.

Sie können das Äquivalent von verwenden zip -j zipfile.zip path1/file1 path2/file2 ..., um zu vermeiden, dass die Zwischenpfade des Unterverzeichnisses in die ZIP-Datei aufgenommen werden.

Wenn Sie diese Dateien von einem Webserver aus bereitstellen (ich bin mir nicht ganz sicher, ob das relevant ist), ist es trivial, diese Struktur zugunsten eines virtuellen Verzeichnisses mit Umschreiberegeln in Apache2 auszublenden. Ich würde davon ausgehen, dass dies auch für Nginx gilt.


Die *Erweiterung ist erfolgreich, es sei denn, Sie haben nicht genügend Arbeitsspeicher. Wenn Sie jedoch das Stapelgrößenlimit erhöhen (unter Linux) oder eine Shell verwenden, in die bereits etwas integriert mvist oder integriert werden kann (ksh93, zsh), schlägt der execve()Systemaufruf möglicherweise mit einem E2BIG-Fehler fehl.
Stéphane Chazelas

@StéphaneChazelas ja ok, meine Wortwahl wäre vielleicht besser gewesen, aber der Nettoeffekt für den Benutzer ist ähnlich. Ich werde sehen, ob ich die Wörter leicht ändern kann, ohne an Komplexität zu verlieren.
Roaima

Sie sind nur neugierig, wie Sie diese ZIP-Datei dekomprimieren würden, wenn Sie die Pfade zwischen den Unterverzeichnissen nicht einschließen würden, ohne auf die von Ihnen diskutierten Probleme zu stoßen?
Octopus

1
@Octopus Das OP gibt an, dass die ZIP-Datei " ausgewählte Dateien, angegeben durch die Liste der Dateinamen " enthält.
Roaima

Ich würde empfehlen, zip -j - ...den Ausgabestream direkt über die Netzwerkverbindung des Clients zu verwenden und weiterzuleiten zip -j zipfile.zip .... Wenn Sie eine aktuelle Zip-Datei auf die Festplatte schreiben, wird der Datenpfad von der Festplatte gelesen -> komprimieren -> auf die Festplatte schreiben -> von der Festplatte lesen -> an den Client senden. Damit können Sie Ihre Festplatten-E / A-Anforderungen verdreifachen, indem Sie Daten von Festplatte-> Komprimieren-> An Client senden.
Andrew Henle

5

Ich betreibe eine Website, die eine Datenbank für Filme, Fernsehen und Videospiele verwaltet. Für jedes dieser Bilder gibt es mehrere Bilder mit einem Fernseher, die Dutzende von Bildern pro Show enthalten (z. B. Episodenschnappschüsse usw.).

Am Ende stehen viele Bilddateien. Irgendwo im Bereich von 250.000+. Diese werden alle in einem eingebauten Blockspeicher gespeichert, bei dem die Zugriffszeit angemessen ist.

Mein erster Versuch, die Bilder zu speichern, war in einem einzigen Ordner als /mnt/images/UUID.jpg

Ich bin auf die folgenden Herausforderungen gestoßen.

  • lsüber ein fernterminal würde nur hängen bleiben. Der Prozess würde zum Zombie werden und CTRL+Cihn nicht brechen.
  • bevor ich diesen Punkt erreiche, lswürde jeder Befehl schnell den Ausgabepuffer füllen und CTRL+Cdas endlose Scrollen nicht stoppen.
  • Das Komprimieren von 250.000 Dateien aus einem einzelnen Ordner dauerte ca. 2 Stunden. Sie müssen den vom Terminal getrennten Befehl zip ausführen, da Sie sonst bei einer Verbindungsunterbrechung erneut von vorne beginnen müssen.
  • Ich würde nicht riskieren, die Zip-Datei unter Windows zu verwenden.
  • Der Ordner wurde schnell zu einer Zone, in der keine Menschen erlaubt waren.

Schließlich musste ich die Dateien in Unterordnern speichern, wobei ich die Erstellungszeit zum Erstellen des Pfads verwendete. Wie /mnt/images/YYYY/MM/DD/UUID.jpg. Dadurch wurden alle oben genannten Probleme behoben, und ich konnte ZIP-Dateien erstellen, die auf ein Datum abzielten.

Wenn die einzige Kennung für eine Datei eine numerische Nummer ist, werden diese Nummern in der Regel nacheinander ausgeführt. Warum nicht gruppieren sie durch 100000, 10000und 1000.

Wenn Sie beispielsweise eine Datei mit dem Namen haben, lautet 384295.txtder Pfad:

/mnt/file/300000/80000/4000/295.txt

Wenn Sie wissen, werden Sie einige Millionen erreichen. Verwenden Sie 0Präfixe für 1.000.000

/mnt/file/000000/300000/80000/4000/295.txt

1

Schreiben Sie eine Textdatei von Web Scrape (sollte nicht durch die Anzahl der Dateien im Ordner beeinflusst werden).

Um eine neue Datei zu erstellen, muss die Verzeichnisdatei nach ausreichend freiem Speicherplatz für den neuen Verzeichniseintrag durchsucht werden. Wenn sich kein Speicherplatz befindet, der groß genug ist, um den neuen Verzeichniseintrag zu speichern, wird er am Ende der Verzeichnisdatei platziert. Wenn die Anzahl der Dateien in einem Verzeichnis zunimmt, nimmt auch die Zeit zum Durchsuchen des Verzeichnisses zu.

Solange die Verzeichnisdateien im Systemcache verbleiben, wird die Leistung dadurch nicht beeinträchtigt. Wenn die Daten jedoch freigegeben werden, kann das Lesen der Verzeichnisdatei (normalerweise stark fragmentiert) von der Festplatte einige Zeit in Anspruch nehmen. Eine SSD verbessert dies, aber für ein Verzeichnis mit Millionen von Dateien kann es immer noch zu einer spürbaren Leistungsbeeinträchtigung kommen.

Komprimieren Sie die ausgewählten Dateien anhand der Liste der Dateinamen.

Dies erfordert wahrscheinlich auch zusätzliche Zeit in einem Verzeichnis mit Millionen von Dateien. In einem Dateisystem mit gehashten Verzeichniseinträgen (wie EXT4) ist dieser Unterschied minimal.

Beeinträchtigt das Speichern von bis zu zehn Millionen Dateien in einem Ordner die Leistung der oben genannten Vorgänge oder die allgemeine Systemleistung anders als das Erstellen eines Baums von Unterordnern für die Dateien, in denen sie gespeichert werden sollen?

Ein Baum von Unterordnern weist keine der oben genannten Leistungsnachteile auf. Wenn das zugrunde liegende Dateisystem so geändert wird, dass es keine Hash-Dateinamen hat, funktioniert die Baummethode weiterhin gut.


1

Erstens: Verhindern Sie, dass 'ls' mit 'ls -U' sortiert, und aktualisieren Sie Ihren ~ / bashrc möglicherweise mit 'alias ls = "ls -U"' oder ähnlichem.

Für Ihre große Dateigruppe können Sie dies folgendermaßen ausprobieren:

  • Erstellen Sie eine Reihe von Testdateien

  • Überprüfen Sie, ob viele Dateinamen Probleme verursachen

  • Verwenden Sie das Parmeter-Batching von xargs und das (Standard-) Verhalten von zip zum Hinzufügen von Dateien zu einer Zip, um Probleme zu vermeiden.

Das hat gut funktioniert:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.