Verwenden von GNU parallel zu Split

9

Ich lade eine ziemlich gigantische Datei in eine Postgresql-Datenbank. Dazu verwende ich zuerst splitin der Datei kleinere Dateien (jeweils 30 GB) und lade dann jede kleinere Datei mit GNU Parallelund in die Datenbank psql copy.

Das Problem ist, dass das Teilen der Datei ungefähr 7 Stunden dauert und dann eine Datei pro Kern geladen wird. Was ich brauche, ist eine Möglichkeit, splitden Dateinamen jedes Mal, wenn er mit dem Schreiben einer Datei fertig ist, in die Standardausgabe zu drucken, damit ich sie weiterleiten kann, Parallelund er beginnt mit dem Laden der Dateien, wenn er splitfertig geschrieben ist. Etwas wie das:

split -l 50000000 2011.psv carga/2011_ | parallel ./carga_postgres.sh {}

Ich habe die splitManpages gelesen und kann nichts finden. Gibt es eine Möglichkeit, dies mit splitoder einem anderen Tool zu tun ?

Topo
quelle

Antworten:

13

Verwenden Sie --pipe:

cat 2011.psv | parallel --pipe -l 50000000 ./carga_postgres.sh

Es erfordert, dass ./carga_postgres.sh aus stdin und nicht aus einer Datei liest, und ist für GNU Parallel Version <20130222 langsam.

Wenn Sie nicht genau 50000000 Zeilen benötigen, ist der --block schneller:

cat 2011.psv | parallel --pipe --block 500M ./carga_postgres.sh

Dadurch werden Blöcke mit einer Aufteilung von ca. 500 MB auf \ n übergeben.

Ich weiß nicht, was ./carga_postgres.sh enthält, aber ich vermute, es enthält psql mit Benutzername Passwort. In diesem Fall möchten Sie möglicherweise GNU SQL (das Teil von GNU Parallel ist) verwenden:

cat 2011.psv | parallel --pipe --block 500M sql pg://user:pass@host/db

Der Hauptvorteil besteht darin, dass Sie keine temporären Dateien speichern müssen, sondern alle Dateien im Speicher / in den Pipes behalten können.

Wenn ./carga_postgres.sh nicht aus stdin lesen kann, sondern aus einer Datei lesen muss, können Sie es in einer Datei speichern:

cat 2011.psv | parallel --pipe --block 500M "cat > {#}; ./carga_postgres.sh {#}"

Große Aufträge scheitern oft auf halbem Weg. GNU Parallel kann Ihnen helfen, indem es die fehlgeschlagenen Jobs erneut ausführt:

cat 2011.psv | parallel --pipe --block 500M --joblog my_log --resume-failed "cat > {#}; ./carga_postgres.sh {#}"

Wenn dies fehlschlägt, können Sie die oben genannten Schritte erneut ausführen. Es werden die Blöcke übersprungen, die bereits erfolgreich verarbeitet wurden.

Ole Tange
quelle
1
Wenn Sie eine neuere Version von GNU Parallel> 20140422 haben, verwenden Sie die Antwort von @ RobertB mit --pipepart. Wenn dies nicht direkt funktioniert, prüfen Sie, ob --fifo oder --cat Ihnen helfen können.
Ole Tange
2

Warum nicht --pipe AND --pipepart mit GNU Parallel verwenden? Dadurch entfällt die zusätzliche Katze und es werden direkte Lesevorgänge von der Datei auf der Festplatte gestartet:

parallel --pipe --pipepart -a 2011.psv --block 500M ./carga_postgres.sh
Robert B.
quelle
1

Ich fand die Antworten hier gepostet Weg zu komplex sein , so dass ich auf Stack - Überlauf gefragt , und ich habe diese Antwort:

Wenn Sie verwenden GNU split, können Sie dies mit der --filterOption tun

'--filter = Befehl'
Mit dieser Option schreiben Sie nicht einfach in jede Ausgabedatei, sondern über eine Pipe in den angegebenen Shell-Befehl für jede Ausgabedatei. Der Befehl sollte die Umgebungsvariable $ FILE verwenden, die für jeden Aufruf des Befehls auf einen anderen Namen der Ausgabedatei festgelegt wird.

Sie können ein Shell-Skript erstellen, das eine Datei erstellt und carga_postgres.sh am Ende im Hintergrund startet

#! /bin/sh

cat >$FILE
./carga_postgres.sh $FILE &

und verwenden Sie dieses Skript als Filter

split -l 50000000 --filter=./filter.sh 2011.psv
Topo
quelle
0

Eine Alternative zum splitDrucken der Dateinamen besteht darin, zu erkennen, wann die Dateien fertig sind. Unter Linux können Sie die Funktion inotify und insbesondere das inotifywaitDienstprogramm verwenden.

inotifywait -m -q -e close_write --format %f carga | parallel ./carga_postgres.sh &
split -l 50000000 2011.psv carga/2011_

Sie müssen inotifywaitmanuell töten . Es automatisch zu töten ist etwas schwierig, da es eine mögliche Rennbedingung gibt: Wenn Sie es töten, sobald splites fertig ist, hat es möglicherweise Ereignisse erhalten, die es noch nicht gemeldet hat. Um sicherzustellen, dass alle Ereignisse gemeldet werden, zählen Sie die übereinstimmenden Dateien.

{
  sh -c 'echo $PPID' >inotifywait.pid
  exec inotifywait -m -q -e close_write --format %f carga
} | tee last.file \
  | parallel ./carga_postgres.sh &
split -l 50000000 2011.psv carga/2011_
(
  set carga/2011_??; eval "last_file=\${$#}"
  while ! grep -qxF "$last_file" last.file; do sleep 1; done
)
kill $(cat inotifywait.pid)
Gilles 'SO - hör auf böse zu sein'
quelle