Wie löse ich die Anzahl der Linux-Unterverzeichnisse?

9

Ich habe eine Website, auf der Benutzerprofilbilder gespeichert werden. Jedes Bild wird in einem benutzerspezifischen Verzeichnis (Linux) gespeichert. Derzeit habe ich einen Kundenstamm von mehr als 30, was bedeutet, dass ich mehr als 30 Ordner haben werde. Meine aktuelle Linux-Box (ext2 / ext3) unterstützt jedoch nicht das Erstellen von mehr als 32000 Verzeichnissen. Wie komme ich daran vorbei? Sogar YouTube-Leute haben das gleiche Problem mit Video-Thumbnails. Aber sie haben es gelöst, indem sie zu ReiserFS gewechselt sind. Können wir keine bessere Lösung haben?

Update: Auf die Frage im IRC wurde nach einem Upgrade auf ext4 gefragt, das ein Limit von 64.000 hat, und natürlich können Sie auch darüber hinwegkommen . Oder Kernel-Hacking, um das Limit zu ändern.

Update: Wie wäre es, wenn Sie die Benutzerbasis basierend auf dem Benutzer-ID-Bereich in Ordner aufteilen. Das bedeutet 1-1000 in einem Ordner, 1000-2000 in dem anderen. Das scheint einfach zu sein. Was sagst du, Leute?

Ehrlich gesagt, gibt es keinen anderen Weg?

Keine da
quelle
1
Warum möchten Sie das Dateisystem nicht ändern? Wenn dies eine Einschränkung von ext2 / 3 ist, haben Sie keine andere Änderung als das Ändern des Dateisystems oder das Aufteilen des aktuellen FS in kleinere FSs (mehr verschiedene Einhängepunkte).
Manuel Faux
1
Manuel: Wenn er das Dateisystem ändert, bindet er einen bestimmten FS an seine Anwendung. Obwohl dies die Antwort sein könnte, würde ich sagen, dass dies wahrscheinlich ein Problem ist, das auf Anwendungsebene gelöst werden muss. Wenn Sie den Kernel oder das Dateisystem hacken müssen, gehen Sie wahrscheinlich den falschen Weg, es sei denn, es handelt sich um ganz besondere Anforderungen.
Kyle Brandt

Antworten:

16

Diese Begrenzung gilt pro Verzeichnis und nicht für das gesamte Dateisystem. Sie können sie also umgehen, indem Sie die Dinge weiter unterteilen. Anstatt beispielsweise alle Benutzer-Unterverzeichnisse im selben Verzeichnis zu haben, teilen Sie sie nach den ersten beiden Zeichen des Namens auf, sodass Sie Folgendes haben:

top_level_dir
|---aa
|   |---aardvark1
|   |---aardvark2
|---da
|   |---dan
|   |---david
|---do
    |---don

Noch besser wäre es, eine Art Hash der Namen zu erstellen und diesen für die Division zu verwenden. Auf diese Weise erhalten Sie eine bessere Verteilung auf die Verzeichnisse, anstatt im Beispiel der Anfangsbuchstaben "da" sehr voll und "zz" vollständig leer zu sein. Wenn Sie beispielsweise den Namen CRC oder MD5 verwenden und die ersten 8 Bits verwenden, erhalten Sie Folgendes:

top_level_dir
|---00
|   |---some_username
|   |---some_username
|---01
|   |---some_username
...
|---FF
|   |---some_username

Dies kann nach Bedarf auf weitere Tiefen erweitert werden, z. B. wenn der Benutzername kein Hashwert ist:

top_level_dir
|---a
|   |---a
|       |---aardvark1
|       |---aardvark2
|---d
    |---a
    |   |---dan
    |   |---david
    |---o
        |---don

Diese Methode wird an vielen Stellen verwendet, z. B. im Squid-Cache, um Ludwigs Beispiel zu kopieren, und in den lokalen Caches von Webbrowsern.

Eine wichtige Sache, die Sie beachten sollten, ist, dass Sie mit ext2 / 3 auf Leistungsprobleme stoßen, bevor Sie sich ohnehin dem Grenzwert von 32.000 nähern, da Verzeichnisse linear durchsucht werden. Durch das Verschieben in ein anderes Dateisystem (z. B. ext4 oder reiser) wird diese Ineffizienz beseitigt (reiser durchsucht Verzeichnisse mit einem binär aufgeteilten Algorithmus, damit lange Verzeichnisse viel effizienter verarbeitet werden, ext4 kann dies auch tun) sowie das feste Limit pro Verzeichnis.

David Spillett
quelle
Die Fragebeschreibung wurde so aktualisiert, dass sie Folgendes enthält: "Update: Wie wäre es, wenn Sie die Benutzerbasis basierend auf dem Benutzer-ID-Bereich in Ordner aufteilen. Das bedeutet 1-1000 in einem Ordner, 1000-2000 in dem anderen. Dies scheint einfach zu sein. Was sagst du?"
Keine-da
1
Das würde gut funktionieren und wäre effizienter als ein Hash, wenn die Benutzer im Allgemeinen anhand der Benutzer-ID anstelle des (oder auch des) Benutzernamens identifiziert würden. Wenn Sie sie jedoch an anderer Stelle im System immer mit Namen referenzieren, müssen Sie überall zusätzliche Namens-> ID-Suchvorgänge hinzufügen.
David Spillett
Danke David! Ich habe sogar eine andere Lösung ausprobiert. Ich habe kaum 4 Ordner mit den Bereichen 1-30000, 30000-60000 usw. erstellt. Ich denke, das Abrufen einer Datei aus einem so großen Verzeichnis dauert länger als aus einem Verzeichnis mit 1000 Dateien (vorheriger Ansatz). Was sagst du?
Keine-da
1
Das hängt vom Dateisystem ab. Wenn Sie ext2 oder ext3 verwenden, würde ich viel weniger als 30.000 pro Verzeichnis empfehlen. Einige Tools geben Warnungen über 10.000 aus. Sie können die Verzeichnisindizierung in ext3 / 4 aktivieren, um Folgendes zu unterstützen: tune2fs -O dir_index / dev / <volumename>, aber ich würde hier nur empfehlen, die Anzahl der Objekte in einem Verzeichnis niedriger zu halten (ein paar Tausend oder weniger?) .
David Spillett
@Maddy, Sie möchten diese Lösung aufgrund anderer Einschränkungen bei der Verarbeitung einer großen Anzahl von Dateien durch Ext2 / 3. Weitere Informationen finden Sie unter serverfault.com/questions/43133/… . Das Aufteilen von Namen in Buckets als Unterverzeichnisse verringert andere Probleme, auf die Sie möglicherweise gestoßen wären. Beachten Sie, dass dies dieselbe Strategie ist, die Squid verwendet, wenn es den Objektcache zum ersten Mal einrichtet - zum Beispiel 64 Verzeichnisse mit jeweils 64 Verzeichnissen, nur als Beispiel.
Avery Payne
7

Wenn Sie an ext2 / ext3 gebunden sind, besteht die einzige Möglichkeit, die ich sehe, darin, Ihre Daten zu partitionieren. Suchen Sie ein Kriterium, das Ihre Daten in verwaltbare Blöcke ähnlicher Größe aufteilt.

Wenn es nur um die Profilbilder geht, würde ich Folgendes tun:

  1. Verwenden Sie einen Hash (z. B. SHA1) des Bildes
  2. Verwenden Sie den SHA1 als Datei- und Verzeichnisnamen

Der SQUID-Cache macht es beispielsweise so:

f / 4b / 353ac7303854033

Das Verzeichnis der obersten Ebene ist die erste Hex-Ziffer, die zweite Ebene die nächsten beiden Hex-Ziffern und der Dateiname die verbleibenden Hex-Ziffern.

Ludwig Weinzierl
quelle
2

Können wir keine bessere Lösung haben?

Sie haben eine bessere Lösung - verwenden Sie ein anderes Dateisystem, es stehen zahlreiche zur Verfügung, von denen viele für verschiedene Aufgaben optimiert sind. Wie Sie bereits betont haben, ist ReiserFS für die Verarbeitung vieler Dateien in einem Verzeichnis optimiert.

Hier finden Sie einen Vergleich der Dateisysteme.

Seien Sie einfach froh, dass Sie nicht an NTFS hängen bleiben, was für viele Dateien in einem Verzeichnis wirklich miserabel ist. Ich würde JFS als Ersatz empfehlen, wenn Sie nicht Lust haben, den relativ neuen (aber anscheinend stabilen) ext4 FS zu verwenden.

gbjbaanb
quelle
Haben Sie gute Links zur Leistung des NTFS-Dateisystems?
Thorbjørn Ravn Andersen
Ja, abgesehen von der persönlichen Erfahrung mit einer App, die zu lange gedauert hat, um neue Dateien in einem Verzeichnis zu erstellen. (Das Löschen aller Dateien dauerte Stunden) und der Leistungssteigerung der Subversion, indem die Anzahl der Dateien in einem Verzeichnis auf 1000 begrenzt wurde. Oder lesen : support.microsoft.com/kb/130694 Ich glaube nicht, dass sie dies jemals "behoben" haben, da es immer noch als Perf bezeichnet wird. Optimierung für NTFS.
Gbjbaanb
1

Ist das Profilbild klein? Was ist mit dem Einfügen in die Datenbank mit den restlichen Profildaten? Dies ist möglicherweise nicht die beste Option für Sie, aber eine Überlegung wert ...

Hier ist ein (älteres) Microsoft-Whitepaper zum Thema: Zu BLOB oder nicht zu BLOB .

Kyle Brandt
quelle
1

Ich habe eine kleine Webgalerie zusammen gehackt, in der ich eine Variation dieses Problems gefunden habe. Ich hatte "nur" ~ 30.000 Bilder im Cache-Verzeichnis, was sich als ziemlich langsam herausstellte (ext2 verwendet verknüpfte Listen für Verzeichnisindizes, wie ich mich erinnere).

Am Ende habe ich etwas in diese Richtung getan:

def key2path(key):
    hash = md5(key)
    return os.path.join(hash[0], hash[1], key)

Dadurch werden die Daten in 256 Verzeichnisse aufgeteilt, wodurch eine schnelle Verzeichnissuche für jede der drei Ebenen möglich ist.

  • Ich habe mich für die Verwendung von MD5 anstelle von SHA-1 entschieden, da MD5 eine andere Ausgabe garantiert, wenn Sie 12 Bit von 32 ändern. Daher finde ich, dass es gut zu Hash-Benutzernamen, Verzeichnissen und anderen kurzen Dingen passt. Und es ist auch schnell ...
  • Ich schließe nicht den gesamten Hash ein, da er viel zu viele Verzeichnisse erzeugt und den Festplatten-Cache immer wieder effektiv in den Papierkorb wirft.
Morten Siebuhr
quelle
1
Sie könnten wahrscheinlich einen einfacheren Hash wie CRC verwenden, da der Hash nicht wie MD5 oder SHA kryptografisch stark sein muss ... aber der Leistungsunterschied ist wahrscheinlich sowieso vernachlässigbar ...
Sleske
0

Das OpenBSD-verknüpfte Projekt "Epitome" ist keine sofortige Antwort auf Ihr Problem, aber Sie sollten darauf achten , dass Sie später darauf zurückgreifen können.

Epitome ist eine Engine, die Single Instance Storage-, Content Addressable Storage- und Deduplizierungsdienste bereitstellt.

Alle Ihre Daten werden in einem Datenspeicher als Hash-Blöcke gespeichert, wodurch nicht eindeutige Blöcke entfernt werden, um die Speicherplatznutzung zu verringern, und Sie können den Speichermechanismus im Wesentlichen vergessen, da Sie den Inhalt einfach per UUID aus dem Datenspeicher anfordern können.

Epitome ist derzeit experimentell, aber etwas, das Sie für die Zukunft beachten sollten.

Muhen
quelle
0

Im Allgemeinen möchten Sie vermeiden, Verzeichnisse mit einer großen Anzahl von Dateien / Verzeichnissen zu haben. Der Hauptgrund ist, dass die Platzhaltererweiterung in der Befehlszeile zu "Zu vielen Argumenten" -Fehlern führt, die beim Versuch, mit diesen Verzeichnissen zu arbeiten, zu großen Schmerzen führen.

Suchen Sie nach einer Lösung, die einen tieferen, aber schmaleren Baum ergibt, z. B. indem Sie Unterordner erstellen, wie andere beschrieben haben.

Thorbjørn Ravn Andersen
quelle
0

Wir hatten ein ähnliches Problem. Die Lösung besteht - wie bereits erwähnt - darin, eine Hierarchie von Verzeichnissen zu erstellen.

Wenn Sie eine komplexe Anwendung haben, die auf einer flachen Verzeichnisstruktur basiert, müssen Sie wahrscheinlich viel patchen. Es ist also gut zu wissen, dass es eine Problemumgehung gibt. Verwenden Sie Symlinks, die nicht das erwähnte 32k-Limit haben. Dann haben Sie genügend Zeit, um die App zu reparieren ...

Karoly Horvath
quelle
0

Warum nicht einen Zeitstempel-Ansatz verwenden und dann eine Überlaufoption haben?

Zum Beispiel

Angenommen, Ihr Zeitstempel lautet: 1366587600

Lassen Sie die letzten 2 Ziffern weg (sonst wird es einfach etwas lächerlich). Teilen Sie den Stempel in 4er-Sets auf (die Verzeichnisanzahl sollte nicht mehr als 9999 erreichen - wenn Sie möchten, können Sie ihn auch anders trennen).

Dies sollte Ihnen so etwas hinterlassen:

/files/1366/5876/

Überprüfen Sie dann auch den Betrag innerhalb des Verzeichnisses vor dem Hochladen. Wenn eine große Anzahl von Uploads erfolgt (z. B. 32000 + pro 100 Sekunden), wiederholen Sie das Verzeichnis in Sekunden oder Buchstaben, z. B.:

/files/1366/5876/a/file.txt

oder

/files/1366/5876/00/file.txt

Protokollieren Sie dann den Zeitstempel + Buchstaben oder den vollständigen Pfadcode zusammen mit dem Benutzer in einer Datenbank, und Sie sollten eingestellt sein.

Pfadstempel: 1366587600 oder 13665876a (wenn Sie Buchstaben verwenden).

Dies führt zwar zu einer großen Anzahl von Verzeichnissen, kann jedoch für die Bearbeitung von Dateirevisionen sehr nützlich sein. Wenn ein Benutzer beispielsweise ein neues Profilbild verwenden möchte, haben Sie immer noch die alte zeitgestempelte Version der älteren Version, falls er die Änderungen rückgängig machen möchte (nicht nur überschrieben).

Feuerwehrmann
quelle
0

Ich würde vorschlagen, zu entscheiden, wie viele maximale Unterverzeichnisse Sie im übergeordneten Ordner haben möchten (oder können).

Dann müssen Sie Ihre Benutzer-ID konvertieren, damit sie bei 1 beginnt.

Dann können Sie tun: modulo = currentId % numberOfSubdirectories

moduloenthält jetzt Ihre Unterverzeichnisnummer, die niemals größer sein wird, als numberOfSubdirectoriesSie ausgewählt haben.

Machen Sie mit Modulo, was Sie wollen, zum Beispiel Hash.

Auch auf diese Weise werden Unterverzeichnisse linear gefüllt.

in vitro
quelle