Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung

Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung

Sehr geehrte Community, dieser Artikel wird sich mit der effektiven Speicherung und Abgabe von Hunderten Millionen kleiner Dateien befassen. Auf dieser Stufe wird eine endgültige Lösung für POSIX-kompatible Dateisysteme mit vollständiger Unterstützung für Sperren, einschließlich Cluster-Sperren, angeboten, und anscheinend sogar schon ohne Workarounds.

Deshalb habe ich einen eigenen spezialisierten Server geschrieben.
Im Verlauf der Umsetzung dieser Aufgabe gelang es, das Hauptproblem zu lösen und gleichzeitig den Speicherplatz und den Arbeitsspeicher zu sparen, den unser Cluster-Dateisystem gnadenlos beanspruchte. Tatsächlich ist eine solche Anzahl von Dateien schädlich für jedes Cluster-Dateisystem.

Die Idee ist folgende:

Einfach gesagt, über den Server werden kleine Dateien hochgeladen, sie werden direkt im Archiv gespeichert und ebenso aus diesem gelesen, während größere Dateien daneben abgelegt werden. Schema: 1 Ordner = 1 Archiv, insgesamt haben wir mehrere Millionen Archive mit kleinen Dateien, anstatt mehrere Hundert Millionen Dateien. Und all dies wurde vollumfänglich umgesetzt, ohne irgendwelche Skripte und ohne die Dateien in tar/zip-Archive zu stapeln.

Ich werde versuchen, mich kürzer zu fassen und entschuldige mich im Voraus, wenn der Beitrag langatmig wird.

Alles begann damit, dass ich keinen geeigneten Server auf der Welt finden konnte, der Daten, die über das HTTP-Protokoll empfangen wurden, direkt in Archive speichern konnte, ohne die Nachteile, die herkömmlichen Archiven und Objektspeichern eigen sind. Der Grund für die Suche war ein auf große Maßstäbe angewachsener Origin-Cluster von 10 Servern, in dem bereits 250.000.000 kleine Dateien angesammelt waren, und die Wachstumsrate wollte nicht aufhören.

Für diejenigen, die keine Artikel mögen, hier eine kurze Dokumentation:

hier und hier.

Und Docker gleich mit, momentan gibt es nur die Option zusammen mit Nginx drin, für alle Fälle:

docker run -d --restart=always -e host=localhost -e root=\/var\/storage \n-v \/var\/storage:\/var\/storage --name wzd -p 80:80 eltaline\/wzd

Weiter:

Wenn es sehr viele Dateien gibt, sind erhebliche Ressourcen erforderlich, wobei das Frustrierendste ist, dass ein Teil davon umsonst verloren geht. Zum Beispiel benötigt ein Cluster-Dateisystem (in diesem Fall – MooseFS) für eine Datei, unabhängig von ihrer tatsächlichen Größe, immer mindestens 64 KB. Das bedeutet, dass für Dateien mit einer Größe von 3, 10 oder 30 KB auf der Festplatte jeweils 64 KB erforderlich sind. Wenn es viertel Milliarde Dateien gibt, verlieren wir zwischen 2 und 10 Terabyte. Unendlich viele neue Dateien zu erstellen, wird nicht möglich sein, da es in MooseFS eine Einschränkung gibt: nicht mehr als 1 Milliarde bei einer Replik jeder Datei.

Mit zunehmender Anzahl von Dateien wird viel RAM für die Metadaten benötigt. Auch häufige große Dumps der Metadaten fördern den Verschleiß von SSD-Speichern.

Server wZD. Wir bringen Ordnung auf die Festplatten.

Der Server ist in der Programmiersprache Go geschrieben. Zunächst musste ich die Anzahl der Dateien reduzieren. Wie kann ich das erreichen? Durch Archivierung, aber in diesem Fall ohne Kompression, da meine Dateien durchweg komprimierte Bilder sind. BoltDB war eine hilfreiche Lösung, die ich zudem von ihren Nachteilen befreien musste, was in der Dokumentation festgehalten ist.

Insgesamt sind anstelle von viertel Milliarde Dateien in meinem Fall nur noch 10 Millionen Bolt-Archive übrig geblieben. Wenn ich die aktuelle Struktur der Verzeichnisse, die mit Dateien gefüllt sind, ändern könnte, wäre es möglicherweise möglich, die Anzahl auf etwa 1 Million Dateien zu reduzieren.

Alle kleinen Dateien werden in Bolt-Archive verpackt, die automatisch die Namen der Verzeichnisse erhalten, in denen sie sich befinden, während alle großen Dateien neben den Archiven bleiben; es macht keinen Sinn, sie zu verpacken, dies ist konfigurierbar. Kleine — archivieren wir, große — lassen wir unverändert. Der Server funktioniert sowohl mit den einen als auch mit den anderen transparent.

Architektur und Besonderheiten des Servers wZD.

Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung

Der Server funktioniert unter den Betriebssystemen Linux, BSD, Solaris und OSX. Ich habe ihn nur für die AMD64-Architektur unter Linux getestet, aber er sollte auch für ARM64, PPC64, MIPS64 geeignet sein.

Hauptmerkmale:

  • Multithreading;
  • Multiserver, der Fehlertoleranz und Lastenverteilung gewährleistet;
  • Maximale Transparenz für den Benutzer oder Entwickler;
  • Unterstützte HTTP-Methoden: GET, HEAD, PUT und DELETE;
  • Management des Verhaltens beim Lesen und Schreiben über Client-Header;
  • Unterstützung für flexibel konfigurierbare virtuelle Hosts;
  • Unterstützung von CRC zur Gewährleistung der Datenintegrität beim Schreiben/Lesen;
  • Halb dynamische Puffer für minimalen Speicherverbrauch und optimale Netzwerkeinstellungen;
  • Verzögerte Datenkompaktion;
  • Zusätzlich wird ein multithreaded Archivator wZA für die Migration von Dateien ohne Unterbrechung des Dienstes angeboten.

Echte Erfahrung:

Ich habe den Server und den Archivator lange Zeit mit Live-Daten entwickelt und getestet, jetzt funktioniert er erfolgreich in einem Cluster mit 250.000.000 kleinen Dateien (Bildern), die in 15.000.000 Verzeichnissen auf separaten SATA-Diskern angeordnet sind. Das Cluster aus 10 Servern stellt einen Origin-Server dar, der hinter einem CDN-Netzwerk installiert ist. Für seine Wartung werden 2 Nginx-Server + 2 wZD-Server eingesetzt.

Denjenigen, die diesen Server nutzen möchten, sei geraten, die Verzeichnisstruktur vor der Verwendung zu planen, sofern dies anwendbar ist. Ich möchte sofort klarstellen, dass der Server nicht dafür gedacht ist, alles in ein einziges Bolt-Archiv zu stecken.

Leistungstest:

Je kleiner die Größe der archivierten Datei, desto schneller erfolgen die GET- und PUT-Operationen damit. Lassen Sie uns die gesamte Schreibzeit eines HTTP-Clients in normale Dateien und in Bolt-Archive sowie das Lesen vergleichen. Es wird mit Dateien von 32 KB, 256 KB, 1024 KB, 4096 KB und 32768 KB gearbeitet.

Bei der Arbeit mit Bolt-Archiven wird die Integrität der Daten jeder Datei überprüft (es wird CRC verwendet), und sowohl vor der Aufnahme als auch nach der Aufnahme erfolgt ein sofortiges Lesen und Neuberechnen. Dies verursacht natürlich Verzögerungen, aber das Wichtigste ist die Datensicherheit.

Ich habe Leistungstests auf SSD-Speichermedien durchgeführt, da die Tests auf SATA-Diskern keinen klaren Unterschied zeigen.

Diagramme zu den Testergebnissen:

Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung
Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung

Wie zu sehen ist, ist der Zeitunterschied beim Lesen und Schreiben zwischen archivierten und nicht archivierten Dateien für kleine Dateien gering.

Ein ganz anderes Bild erhalten wir jedoch bei Tests mit dem Lesen und Schreiben von Dateien mit einer Größe von 32 MB:

Effektive Speicherung von Hunderten Millionen kleiner Dateien. Self-Hosted Lösung

Der Zeitunterschied beim Lesen der Dateien liegt im Bereich von 5-25 ms. Beim Schreiben ist die Situation schlechter, der Unterschied beträgt etwa 150 ms. Aber in diesem Fall ist es auch nicht notwendig, große Dateien hochzuladen, das macht einfach keinen Sinn; sie können unabhängig von den Archiven leben.

*Technisch kann dieser Server auch für Aufgaben verwendet werden, die NoSQL erfordern.

Wesentliche Methoden zur Arbeit mit dem wZD-Server:

Hochladen einer normalen Datei:

curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpg

Hochladen einer Datei in ein Bolt-Archiv (wenn der Serverparameter fmaxsize, der die maximale Größe der Datei bestimmt, die in das Archiv aufgenommen werden kann, nicht überschritten wird; falls überschritten, wird die Datei wie gewohnt neben dem Archiv hochgeladen):

curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpg

Datei herunterladen (wenn im Verzeichnis und im Archiv Dateien mit denselben Namen vorhanden sind, wird beim Herunterladen standardmäßig die nicht archivierte Datei priorisiert):

curl -o test.jpg http://localhost/test/test.jpg

Datei aus dem Bolt-Archiv herunterladen (erzwingen):

curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpg

Eine Beschreibung anderer Methoden finden Sie in der Dokumentation.

Dokumentation wZD
Dokumentation wZA

Der Server unterstützt derzeit nur das HTTP-Protokoll, HTTPS funktioniert momentan nicht. Die POST-Methode wird ebenfalls nicht unterstützt (es ist noch nicht entschieden, ob sie benötigt wird oder nicht).

Wer im Quellcode stöbert, wird dort einen Keks finden, den nicht jeder mag. Ich habe den Hauptcode jedoch nicht an die Funktionen des Web-Frameworks gebunden, außer dem Interrupt-Handler, sodass ich ihn später schnell auf jede Engine umschreiben kann.

ToDo:

  • Entwicklung eines eigenen Replikators und Distributors + Geo für die Nutzung in großen Systemen ohne Cluster-Dateisysteme (alles professionell).
  • Die Möglichkeit einer vollständigen reversiblen Wiederherstellung der Metadaten im Falle eines vollständigen Verlusts (bei Verwendung des Distributors).
  • Natives Protokoll für die Möglichkeit der Nutzung permanenter Netzwerkverbindungen und Treiber für verschiedene Programmiersprachen.
  • Erweiterte Nutzungsmöglichkeiten der NoSQL-Komponente.
  • Kompression verschiedener Typen (gzip, zstd, snappy) für Dateien oder Werte innerhalb von Bolt-Archiven und für reguläre Dateien.
  • Verschlüsselung verschiedener Typen für Dateien oder Werte innerhalb von Bolt-Archiven und für reguläre Dateien.
  • Verspätete serverseitige Videokonvertierung, auch auf GPU.

Ich bin fertig, ich hoffe, dieser Server wird jemandem nützlich sein, Lizenz BSD-3, Urheberrecht doppelt, da ich ohne das Unternehmen, in dem ich arbeite, den Server nicht geschrieben hätte. Ich bin der alleinige Entwickler. Ich wäre dankbar für gefundene Bugs und Feature-Anfragen.

Quelle: habr.com

60GB SSD 8Gb DDR4