Usare GNU in parallelo con Split


9

Sto caricando un file piuttosto gigantesco in un database postgresql. Per fare ciò uso prima splitnel file per ottenere file più piccoli (30 Gb ciascuno) e quindi carico ogni file più piccolo nel database usando GNU Parallele psql copy.

Il problema è che sono necessarie circa 7 ore per dividere il file e quindi inizia a caricare un file per core. Quello di cui ho bisogno è un modo per dire splitdi stampare il nome del file sull'output std ogni volta che finisce di scrivere un file in modo da poterlo reindirizzare Parallele inizia a caricare i file nel momento in cui splitlo scrivo. Qualcosa come questo:

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

Ho letto le splitpagine man e non riesco a trovare nulla. C'è un modo per farlo con splito con qualsiasi altro strumento?

Risposte:


13

Usa --pipe:

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

Richiede ./carga_postgres.sh per leggere da stdin e non da un file, ed è lento per la versione parallela di GNU <20130222.

Se non hai bisogno esattamente di 50000000 linee, il blocco - è più veloce:

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

In questo modo verranno passati pezzi di circa 500 MB suddivisi su \ n.

Non so cosa contenga ./carga_postgres.sh, ma suppongo che contenga psql con password nome utente. In tal caso potresti voler usare GNU SQL (che fa parte di GNU Parallel):

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

Il vantaggio principale è che non è necessario salvare i file temporanei, ma è possibile conservare tutto in memoria / pipe.

Se ./carga_postgres.sh non è in grado di leggere da stdin, ma deve leggere da un file, è possibile salvarlo in un file:

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

I lavori di grandi dimensioni spesso falliscono a metà strada. GNU Parallel può aiutarti eseguendo nuovamente i lavori non riusciti:

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

In caso contrario, è possibile rieseguire quanto sopra. Salterà i blocchi che sono già stati elaborati correttamente.


1
Se hai una versione più recente di GNU Parallel> 20140422 usa la risposta di @ RobertB con --pipepart. Se ciò non funziona direttamente, vedi se --fifo o --cat possono aiutarti.
Ole Tange,

2

Perché non usare --pipe AND --pipepart con GNU Parallel? Questo elimina il gatto extra e avvia le letture dirette dal file sul disco:

parallel --pipe --pipepart -a 2011.psv --block 500M ./carga_postgres.sh

1

Ho trovato le risposte pubblicate qui per essere complesse, quindi ho chiesto su Stack Overflow e ho ottenuto questa risposta:

Se lo usi GNU split, puoi farlo con l' --filteropzione

'--filter = command'
Con questa opzione, anziché semplicemente scrivere su ciascun file di output, scrivere attraverso una pipe al comando shell specificato per ciascun file di output. Il comando deve utilizzare la variabile di ambiente $ FILE, che è impostata su un nome file di output diverso per ogni invocazione del comando.

È possibile creare uno script di shell, che crea un file e avviare carga_postgres.sh alla fine in background

#! /bin/sh

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

e usa quello script come filtro

split -l 50000000 --filter=./filter.sh 2011.psv

0

Un'alternativa alla splitstampa dei nomi dei file consiste nel rilevare quando i file sono pronti. Su Linux, è possibile utilizzare la funzione inotify , e in particolare l' inotifywaitutilità.

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

Dovrai uccidere inotifywaitmanualmente. Ucciderlo automaticamente è un po 'difficile perché c'è una potenziale condizione di razza: se lo uccidi non appena splitfinisce, potrebbe aver ricevuto eventi che non ha ancora segnalato. Per assicurarsi che vengano segnalati tutti gli eventi, contare i file corrispondenti.

{
  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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.