sed Optimierung (große Dateiänderung basierend auf kleineren Datensätzen)

8

Ich muss mich mit sehr großen Klartextdateien beschäftigen (über 10 Gigabyte, ja ich weiß, es kommt darauf an, was wir groß nennen sollten), mit sehr langen Zeilen.

Meine letzte Aufgabe beinhaltet eine Zeilenbearbeitung basierend auf Daten aus einer anderen Datei.

Die Datendatei (die modifiziert werden sollte) enthält 1500000 Zeilen, von denen jede z. 800 Zeichen lang. Jede Zeile ist eindeutig und enthält nur eine Identitätsnummer, jede Identitätsnummer ist eindeutig)

Die Änderungsdatei ist z.B. 1800 Zeilen lang, enthält eine Identitätsnummer sowie eine Menge und ein Datum, das in der Datendatei geändert werden sollte.

Ich habe gerade die Modifier-Datei (mit Vim regex) in sed umgewandelt, aber sie ist sehr ineffizient.

Nehmen wir an, ich habe eine solche Zeile in der Datendatei:

%Vor%

Und ich muss die Daten im 300 Char-Teil ändern.

Basierend auf der Modifier-Datei habe ich folgende sed-Zeilen:

%Vor%

Also habe ich 1800 Zeilen so.

Aber ich weiß, dass selbst auf einem sehr schnellen Server, wenn ich ein

mache %Vor%

Es ist sehr langsam, weil es jedes Muster x jede Zeile lesen muss.

Gibt es keinen besseren Weg?

Hinweis: Ich bin kein Programmierer, habe nie (in der Schule) über Algorithmen gelernt. Ich kann awk, sed, eine veraltete Version von Perl auf dem Server verwenden.

    
Zsolt Botykai 11.05.2009, 16:27
quelle

6 Antworten

6

Meine vorgeschlagenen Ansätze (in der Reihenfolge der wünschenswert) wäre, diese Daten als zu verarbeiten:

  1. Eine Datenbank (sogar eine einfache SQLite-basierte Datenbank mit einem Index wird viel besser als sed / awk bei einer 10GB-Datei)
  2. Eine flache Datei mit festen Datensatzlängen
  3. Eine flache Datei mit variablen Datensatzlängen

Die Verwendung einer Datenbank kümmert sich um all die kleinen Details, die die Verarbeitung von Textdateien verlangsamen (den Datensatz finden, der Ihnen wichtig ist, die Daten ändern, sie in der DB speichern). Suchen Sie im Fall von Perl nach DBD :: SQLite.

Wenn Sie bei flachen Dateien bleiben möchten, sollten Sie einen Index manuell neben der großen Datei verwalten, damit Sie die Datensatznummern, die Sie bearbeiten müssen, leichter nachschlagen können. Oder, noch besser, vielleicht sind Ihre ID-Nummern Ihre Rekordnummern?

Wenn Sie variable Datensatzlängen haben, würde ich vorschlagen, in feste Datensatzlängen zu konvertieren (da nur Ihre ID eine variable Länge hat). Wenn Sie das nicht können, werden sich möglicherweise vorhandene Daten nie in der Datei bewegen? Dann können Sie den zuvor erwähnten Index beibehalten und bei Bedarf neue Einträge hinzufügen, mit dem Unterschied, dass Sie anstelle des auf die Datensatznummer zeigenden Index jetzt auf die absolute Position in der Datei zeigen.

    
MikeyB 11.05.2009, 17:17
quelle
3

Ich schlage dir ein Programm vor, das in Perl geschrieben ist (da ich kein sed / awk Guru bin und ich nicht weiß, wozu sie genau in der Lage sind).

Ihr "Algorithmus" ist einfach: Sie müssen zuerst eine Hashmap konstruieren, die Ihnen die neue Datenkette geben kann, die für jede ID gilt. Dies wird natürlich durch Lesen der Modifikationsdatei erreicht.

Sobald diese Hasmap eingefügt wurde, können Sie jede Zeile Ihrer Datendatei durchsuchen, die ID in der Mitte der Zeile lesen und die neue Zeile wie oben beschrieben generieren.

Ich bin auch kein Perl-Guru, aber ich denke, dass das Programm ziemlich einfach ist. Wenn Sie Hilfe benötigen, um es zu schreiben, fragen Sie danach: -)

    
yves Baumes 11.05.2009 17:05
quelle
2

Bei Perl sollten Sie substr verwenden, um id_number zu erhalten, besonders wenn id_number eine konstante Breite hat.

%Vor%

Danach, wenn $ id_number im Bereich ist, sollten Sie substr verwenden, um den verbleibenden Text zu ersetzen.

%Vor%

Perls reguläre Ausdrücke sind sehr schnell, aber nicht in diesem Fall.

    
Alexandr Ciornii 11.05.2009 17:18
quelle
1

Mein Vorschlag ist, verwende keine Datenbank. Gut geschriebenes Perl-Skript wird die Datenbank bei dieser Art von Aufgabe in der Größenordnung übertreffen. Vertrau mir, ich habe viele praktische Erfahrungen damit. Sie haben keine Daten in die Datenbank importiert, wenn Perl fertig ist.

Wenn Sie 1500000 Zeilen mit 800 Zeichen schreiben, scheint es 1,2GB für mich. Wenn Sie eine sehr langsame Festplatte (30MB / s) haben, werden Sie sie in 40 Sekunden lesen. Mit besseren 50 - & gt; 24s, 100 - & gt; 12s und so. Aber Perl Hash Lookup (wie Db Join) Geschwindigkeit auf 2 GHz CPU ist über 5 Mlookups / s. Das bedeutet, dass Ihre CPU-gebundene Arbeit in Sekunden erfolgt und Ihre E / A-gebundene Arbeit in einigen Sekunden erfolgt. Wenn es wirklich 10GB ist, ändern sich die Zahlen, aber der Anteil ist gleich.

Sie haben nicht angegeben, ob die Änderung der Daten die Größe ändert oder nicht (wenn Änderungen an Ort und Stelle vorgenommen werden können). Wir gehen daher nicht davon aus und werden als Filter arbeiten. Sie haben nicht angegeben, welches Format Ihrer "Modifier-Datei" und welche Art von Modifikation. Nehmen Sie an, dass es durch Tab getrennt ist, etwa wie folgt:

%Vor%

Wir werden Daten von stdin lesen und in stdout und script schreiben, etwa so:

%Vor%

Auf meinem Laptop dauert es etwa eine halbe Minute für 1,5 Millionen Zeilen, 1800 Lookup-IDs, 1,2 GB Daten. Für 10GB sollte es nicht über 5 Minuten sein. Ist es für Sie angemessen schnell?

Wenn Sie glauben, dass Sie nicht IO-gebunden sind (zum Beispiel wenn Sie NAS verwenden), sondern CPU-gebunden, können Sie etwas Lesbarkeit opfern und ändern:

%Vor%     
Hynek -Pichi- Vychodil 16.05.2009 22:29
quelle
0

Sie sollten fast sicher eine Datenbank verwenden, wie MikeyB schlug vor .

Wenn Sie aus irgendeinem Grund keine Datenbank verwenden möchten, ist die effizienteste Methode eine Hashtabelle, die mit den vorgeschlagenen Änderungen gefüllt wird, wenn die Liste der Änderungen in den Speicher passt (wie derzeit bei 1800 Zeilen) von yves Baumes .

Wenn Sie zu dem Punkt kommen, an dem sogar die Liste der Änderungen zu groß wird, müssen Sie beide Dateien nach ihren IDs sortieren und dann eine Listenzusammenführung durchführen - im Grunde:

  1. Vergleichen Sie die ID an der "Spitze" der Eingabedatei mit der ID an der "Spitze" der Änderungsdatei
  2. Passen Sie den Datensatz entsprechend an, wenn sie
  3. entsprechen
  4. Schreibe es aus
  5. Verwerfen Sie die "oberste" Zeile von der Datei mit der niedrigsten (alphabetisch oder numerisch) ID und lesen Sie eine andere Zeile aus dieser Datei
  6. Gehe zu 1.

Hinter den Kulissen wird eine Datenbank fast sicher eine Listenzusammenführung verwenden, wenn Sie diese Änderung mit einem einzigen SQL UPDATE -Befehl durchführen.

    
j_random_hacker 12.05.2009 12:52
quelle
0

Guter Deal bei der Entscheidung sqloader oder datadump. Das ist der Weg zu gehen.

    
hpavc 12.05.2009 19:04
quelle

Tags und Links