Usando GNU Paralelo Con Split

9

Estoy cargando un archivo bastante gigantesco en una base de datos postgresql. Para hacer esto, primero lo uso spliten el archivo para obtener archivos más pequeños (30 Gb cada uno) y luego cargo cada archivo más pequeño en la base de datos usando GNU Parallely psql copy.

El problema es que toma aproximadamente 7 horas dividir el archivo y luego comienza a cargar un archivo por núcleo. Lo que necesito es una manera de decirle splitque imprima el nombre del archivo a la salida estándar cada vez que termine de escribir un archivo para poder conectarlo Parallely comience a cargar los archivos en el momento en que splittermine de escribirlo. Algo como esto:

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

He leído las splitpáginas del manual y no puedo encontrar nada. ¿Hay alguna manera de hacer esto con splitalguna otra herramienta?

Topo
fuente

Respuestas:

13

Use --pipe:

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

Requiere ./carga_postgres.sh para leer desde stdin y no desde un archivo, y es lento para GNU Parallel versión <20130222.

Si no necesita exactamente 50000000 líneas, el --block es más rápido:

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

Esto pasará fragmentos de alrededor de 500 MB divididos en \ n.

No sé qué contiene ./carga_postgres.sh, pero supongo que contiene psql con contraseña de nombre de usuario. En ese caso, es posible que desee utilizar GNU SQL (que es parte de GNU Parallel):

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

El principal beneficio es que no necesita guardar archivos temporales, sino que puede guardarlos en memoria / canalizaciones.

Si ./carga_postgres.sh no puede leer desde stdin, pero debe leer desde un archivo, puede guardarlo en un archivo:

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

Los trabajos grandes a menudo fallan a la mitad. GNU Parallel puede ayudarlo volviendo a ejecutar los trabajos fallidos:

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

Si esto falla, puede volver a ejecutar lo anterior. Omitirá los bloques que ya se procesaron con éxito.

Ole Tange
fuente
1
Si tiene una versión más nueva de GNU Parallel> 20140422, use la respuesta de @ RobertB con --pipepart. Si eso no funciona directamente, vea si --fifo o --cat pueden ayudarlo.
Ole Tange
2

¿Por qué no usar --pipe AND --pipepart con GNU Parallel? Esto elimina el gato extra e inicia lecturas directas del archivo en el disco:

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

Encontré que las respuestas publicadas aquí son complejas, así que pregunté por Stack Overflow y obtuve esta respuesta:

Si lo usa GNU split, puede hacerlo con la --filteropción

'--filter = command'
Con esta opción, en lugar de simplemente escribir en cada archivo de salida, escriba a través de una tubería al comando de shell especificado para cada archivo de salida. El comando debe usar la variable de entorno $ FILE, que se establece en un nombre de archivo de salida diferente para cada invocación del comando.

Puede crear un script de shell, que crea un archivo e iniciar carga_postgres.sh al final en segundo plano

#! /bin/sh

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

y usar ese script como filtro

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

Una alternativa para splitimprimir los nombres de los archivos es detectar cuándo están listos. En Linux, puede usar la función inotify , y específicamente la inotifywaitutilidad.

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

Tendrás que matar inotifywaitmanualmente. Matarlo automáticamente es un poco difícil porque hay una posible condición de carrera: si lo matas tan pronto como splittermine, es posible que haya recibido eventos que aún no ha informado. Para asegurarse de que se informan todos los eventos, cuente los archivos coincidentes.

{
  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- deja de ser malvado'
fuente