web-dev-qa-db-de.com

Sortieren Sie eine Datei mit einem riesigen Datenvolumen unter Berücksichtigung der Speichereinschränkung

Punkte: 

  • Wir verarbeiten gleichzeitig Tausende von flachen Dateien an einem Tag. 
  • Speicherbeschränkung ist ein Hauptproblem. 
  • Wir verwenden für jeden Dateiprozess einen Thread.
  • Wir sortieren nicht nach Spalten. Jede Zeile (Datensatz) in der Datei wird als eine Spalte behandelt.

Kann nicht tun:

  • Wir können keine Sortierbefehle von Unix/Linux verwenden.
  • Wir können kein Datenbanksystem verwenden, egal wie leicht sie sein können.

Nun können wir nicht einfach alles in eine Sammlung laden und den Sortiermechanismus verwenden. Dadurch wird der gesamte Speicher aufgebraucht und das Programm wird einen Heap-Fehler erhalten.

Wie würden Sie in dieser Situation die Datensätze/Zeilen in einer Datei sortieren?

28
Erika Gomez

Es sieht so aus, als wäre das, was Sie suchen, externe Sortierung .

Grundsätzlich sortieren Sie zuerst kleine Datenblöcke, schreiben diese zurück auf die Festplatte und iterieren dann diese, um alle zu sortieren.

43
phisch

Sie können die Dateien in kleineren Teilen lesen, sortieren und in temporäre Dateien schreiben. Dann lesen Sie zwei davon nacheinander wieder und führen sie zu einer größeren temporären Datei zusammen usw. Wenn nur noch einer übrig ist, haben Sie Ihre Datei sortiert. Im Grunde ist dies der Megresort-Algorithmus, der für externe Dateien ausgeführt wird. Es lässt sich gut mit beliebig großen Dateien skalieren, verursacht jedoch zusätzliche E/A-Vorgänge.

Bearbeiten: Wenn Sie etwas über die wahrscheinlichen Abweichungen der Zeilen in Ihren Dateien wissen, können Sie einen effizienteren Algorithmus (Verteilungssortierung) verwenden. Vereinfacht gesagt, würden Sie die Originaldatei einmal lesen und jede Zeile in eine temporäre Datei schreiben, die nur Zeilen mit demselben ersten Zeichen (oder einem bestimmten Bereich der ersten Zeichen) enthält. Dann durchlaufen Sie alle (jetzt kleinen) temporären Dateien in aufsteigender Reihenfolge, sortieren Sie sie im Speicher und hängen Sie sie direkt an die Ausgabedatei an. Wenn sich herausstellt, dass eine temporäre Datei zu groß ist, um im Speicher sortiert zu werden, können Sie den gleichen Vorgang für das zweite Zeichen in den Zeilen usw. erneut wiederholen. Wenn Ihre erste Partitionierung gut genug war, um ausreichend kleine Dateien zu erzeugen, haben Sie unabhängig von der Größe der Datei nur 100% E/A-Overhead. Im schlimmsten Fall kann sie jedoch viel mehr sein als bei der leistungsmäßig stabilen Zusammenführungssorte.

10
x4u

Trotz Ihrer Einschränkung würde ich die eingebettete Datenbank SQLITE3 verwenden. Wie Sie selbst arbeite ich wöchentlich mit 10-15 Millionen Flat File-Zeilen, und es ist sehr, sehr schnell zu importieren und sortierte Daten zu generieren, und Sie benötigen nur eine kleine kostenlose ausführbare Datei (sqlite3.exe). Zum Beispiel: Nachdem Sie die .exe-Datei heruntergeladen haben, können Sie in einer Eingabeaufforderung Folgendes ausführen:

C:> sqlite3.exe dbLines.db
sqlite> create table tabLines(line varchar(5000));
sqlite> create index idx1 on tabLines(line);
sqlite> .separator '\r\n'
sqlite> .import 'FileToImport' TabLines

dann:

sqlite> select * from tabLines order by line;

or save to a file:
sqlite> .output out.txt
sqlite> select * from tabLines order by line;
sqlite> .output stdout
9
Eduardo

Ich würde einen EC2-Cluster hochfahren und Hadoops MergeSort ausführen.

Edit: Sie sind sich nicht sicher, wie viele Details Sie wünschen oder worauf. EC2 ist die Elastic Compute Cloud von Amazon, mit der Sie virtuelle Server stundenweise zu günstigen Preisen mieten können. Hier ist ihre Website .

Hadoop ist ein Open-Source-MapReduce-Framework für die parallele Verarbeitung großer Datensätze. Ein Job ist ein guter Kandidat für MapReduce, wenn er in Teilmengen aufgeteilt werden kann, die einzeln verarbeitet und anschließend zusammengefügt werden können, üblicherweise durch Sortieren nach Schlüsseln (dh der Divide-and-Conquer-Strategie). Hier ist seine Website .

Wie bei den anderen Plakaten erwähnt, ist auch die Aussensortierung eine gute Strategie. Ich denke, die Art und Weise, wie ich mich zwischen den beiden entscheiden würde, hängt von der Größe der Daten und den Geschwindigkeitsanforderungen ab. Ein einzelner Computer ist wahrscheinlich auf die Bearbeitung einer einzelnen Datei gleichzeitig beschränkt (da der verfügbare Speicher belegt wird). Sehen Sie sich also etwas in EC2 an, wenn Sie Dateien schneller verarbeiten müssen.

8
danben

Wie bereits erwähnt, können Sie in Schritten arbeiten.
Ich möchte das mit meinen eigenen Worten erklären (unterscheidet sich in Punkt 3):

  1. Lesen Sie die Datei sequentiell und verarbeiten Sie jeweils N Datensätze im Speicher (N ist willkürlich, abhängig von Ihrer Speicherbeschränkung und der Anzahl T der temporären Dateien, die Sie möchten).

  2. Sortieren Sie die N Datensätze im Speicher und schreiben Sie sie in eine temporäre Datei. Schleife auf T, bis Sie fertig sind.

  3. Öffnen Sie alle T-Temp-Dateien gleichzeitig, lesen Sie jedoch nur einen Datensatz pro Datei. (Natürlich mit Puffern). Suchen Sie für jeden dieser T-Datensätze den kleineren, schreiben Sie ihn in die endgültige Datei und fahren Sie nur in dieser Datei fort.


Vorteile:

  • Der Verbrauch von memory ist so niedrig wie Sie möchten.
  • Sie führen nur den double-Datenträgerzugriff aus, der mit einer All-in-Memory-Richtlinie verglichen wird. Nicht schlecht! :-)

Beispiel mit Zahlen:

  1. Originaldatei mit 1 Million Datensätzen.
  2. Wählen Sie 100 temporäre Dateien aus. Lesen und sortieren Sie daher 10 000 Datensätze gleichzeitig und legen Sie diese in einer eigenen temporären Datei ab.
  3. Öffnen Sie jeweils die 100-temp-Datei und lesen Sie den ersten Datensatz im Speicher.
  4. Vergleichen Sie die ersten Datensätze, schreiben Sie die kleineren Datensätze und erweitern Sie diese temporäre Datei.
  5. Wiederholen Sie den Schritt 5 eine Million Mal.

BEARBEITET

Sie haben eine Multithread-Anwendung erwähnt, daher frage ich mich ...

Wie wir aus diesen Diskussionen zu diesem Bedarf gesehen haben, führt die Verwendung von weniger Speicher zu einer geringeren Leistung, was in diesem Fall einen dramatischen Faktor darstellt. Daher könnte ich auch vorschlagen, nur einen Thread zu verwenden, um jeweils nur eine Sortierung zu verarbeiten, nicht als Multithread-Anwendung.

Wenn Sie zehn Threads mit jeweils einem Zehntel des verfügbaren Arbeitsspeichers verarbeiten, ist Ihre Leistung schlecht, viel weniger als ein Zehntel der Anfangszeit. Wenn Sie nur einen Thread verwenden und die 9 anderen Anforderungen in eine Warteschlange stellen und diese wiederum bearbeiten, wird Ihre globale Leistung erheblich verbessert, und Sie werden die zehn Aufgaben schneller erledigen.


Nach dem Lesen dieser Antwort: Sortiere eine Datei mit einem riesigen Datenvolumen, wenn der Speicher eingeschränkt ist Ich schlage vor, dass Sie diese Verteilungssortierung berücksichtigen. Es könnte ein großer Gewinn in Ihrem Kontext sein.

Die Verbesserung gegenüber meinem Vorschlag besteht darin, dass Sie nicht alle temporären Dateien gleichzeitig öffnen müssen, sondern nur eine davon. Es rettet deinen Tag! :-)

6
KLE

Sie können die folgende Divide-and-Conquer-Strategie verwenden:

Erstellen Sie eine Funktion H(), die jedem Datensatz in der Eingabedatei eine Nummer zuweisen kann. Für einen Datensatz r2, der hinter einem Datensatz r1 sortiert wird, muss er eine größere Anzahl für r2 zurückgeben als für r1. Verwenden Sie diese Funktion, um alle Datensätze in separate Dateien zu partitionieren, die in den Arbeitsspeicher passen, damit Sie sie sortieren können. Wenn Sie dies getan haben, können Sie die sortierten Dateien einfach zusammenfügen, um eine große sortierte Datei zu erhalten.

Angenommen, Sie haben diese Eingabedatei, in der jede Zeile einen Datensatz darstellt

Alan Smith
Jon Doe
Bill Murray
Johnny Cash

Lassen Sie uns einfach H() so bauen, dass der erste Buchstabe des Datensatzes verwendet wird, so dass Sie bis zu 26 Dateien erhalten können.

<file1>
Alan Smith

<file2>
Bill Murray

<file10>
Jon Doe
Johnny Cash

Jetzt können Sie jede einzelne Datei sortieren. Was würde "Jon Doe" und "Johnny Cash" in <file10> tauschen. Wenn Sie nun die 3 Dateien nur verketten, haben Sie eine sortierte Version der Eingabe.

Beachte, dass du zuerst teilst und später siegst (sortierst). Sie stellen jedoch sicher, dass Sie die Partitionierung so durchführen, dass die resultierenden Teile, die Sie sortieren müssen, sich nicht überschneiden, was das Zusammenführen des Ergebnisses erheblich vereinfacht. 

Die Methode, mit der Sie die Partitionierungsfunktion H() implementieren, hängt stark von der Art Ihrer Eingabedaten ab. Sobald Sie diesen Teil herausgefunden haben, sollte der Rest eine Brise sein.

2
VoidPointer

Wenn Sie lediglich ein external -Datenbanksystem nicht verwenden möchten, können Sie eine eingebettete Datenbank (z. B. Apache Derby ) versuchen. Auf diese Weise erhalten Sie alle Vorteile einer Datenbank ohne Abhängigkeiten der externen Infrastruktur.

2
FRotthowe

Sie können die SQL Lite-Datei db verwenden, die Daten in die db laden und anschließend sortieren und die Ergebnisse für Sie zurückgeben. Vorteile: Sie müssen sich keine Gedanken über den besten Sortieralgorithmus machen Festplattenspeicher, langsamere Verarbeitung . https://sites.google.com/site/arjunwebworld/Home/programming/sorting-large-data-files

0
user2071703

Hier ist eine Möglichkeit, dies zu tun, ohne dass in-side Java umfangreich sortiert werden muss und ohne DB ..__ zu verwenden. Annahmen: Sie haben 1 TB Speicherplatz und Dateien enthalten oder beginnen mit einer eindeutigen Nummer, sind aber nicht sortiert

Teilen Sie die Dateien N mal.

Lesen Sie diese N-Dateien nacheinander und erstellen Sie für jede Zeile/Nummer eine Datei

Benennen Sie diese Datei mit der entsprechenden Nummer. Halten Sie einen Zähler auf dem neuesten Stand, um die Mindestanzahl zu speichern.

Jetzt können Sie den Stammordner der Dateien bereits für die Sortierung nach Namen markieren oder Ihr Programm anhalten, um Ihnen die Zeit zu geben, den Befehl auf Ihrem Betriebssystem auszulösen, um die Dateien nach Namen zu sortieren. Sie können das auch programmgesteuert machen. 

Jetzt haben Sie einen Ordner mit Dateien, die nach ihrem Namen sortiert sind. Verwenden Sie den Zähler, um jede Datei nacheinander zu übernehmen, geben Sie in Ihre OUTPUT-Datei Zahlen ein, und schließen Sie sie.

Wenn Sie fertig sind, haben Sie eine große Datei mit sortierten Nummern.

0
MayurRB

Sie können dies mit nur zwei temporären Dateien - Quelle und Ziel - und so wenig Speicher tun, wie Sie möchten. Im ersten Schritt ist Ihre Quelle die Originaldatei, im letzten Schritt ist das Ziel die Ergebnisdatei.

Bei jeder Iteration:

  • liest aus der Quelldatei einen Datenblock mit halber Größe des Puffers in einen Schiebepuffer; 
  • sortiere den ganzen Puffer
  • in die Zieldatei schreiben Sie die erste Hälfte des Puffers. 
  • verschieben Sie die zweite Hälfte des Puffers an den Anfang und wiederholen Sie den Vorgang 

Behalten Sie ein boolesches Flag, das angibt, ob Sie einige Datensätze in der aktuellen Iteration verschieben mussten .. Wenn das Flag "false" bleibt, wird Ihre Datei sortiert. Wenn es ausgelöst wird, wiederholen Sie den Vorgang unter Verwendung der Zieldatei als Quelle.

Maximale Anzahl von Iterationen: (Dateigröße)/(Puffergröße) * 2

0
user7932299

Ich weiß, dass Sie erwähnt haben, dass Sie keine Datenbank verwenden, egal wie leicht ... also, vielleicht ist dies keine Option. Aber was ist mit hsqldb im Speicher? Senden Sie es, sortieren Sie es nach Abfrage und löschen Sie es. Nur ein Gedanke.

0
PaulP1975