Usando o GNU Parallel With Split

9

Estou carregando um arquivo gigantesco em um banco de dados postgresql. Para fazer isso, primeiro uso split no arquivo para obter arquivos menores (30Gb cada) e, em seguida, carrego cada arquivo menor no banco de dados usando GNU Parallel e psql copy .

O problema é que leva cerca de 7 horas para dividir o arquivo e, em seguida, ele começa a carregar um arquivo por núcleo. O que eu preciso é uma maneira de dizer split para imprimir o nome do arquivo para std saída toda vez que terminar de gravar um arquivo para que eu possa canalizar para Parallel e ele comece a carregar os arquivos no momento split termine de escrevê-lo . Algo parecido com isto:

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

Eu li as páginas split man e não consigo encontrar nada. Existe uma maneira de fazer isso com split ou qualquer outra ferramenta?

    
por Topo 28.02.2013 / 21:28

4 respostas

12

Use --pipe:

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

Ele requer ./carga_postgres.sh para ler de stdin e não de um arquivo, e é lento para a versão GNU Parallel < 20130222.

Se você não precisa exatamente de 50000000 linhas, o --block é mais rápido:

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

Isso vai passar pedaços de cerca de 500MB divididos em \ n.

Eu não sei o que ./carga_postgres.sh contém, mas meu palpite é que ele contém o psql com a senha do nome de usuário. Nesse caso, você pode querer usar o GNU SQL (que faz parte do GNU Parallel):

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

A principal vantagem é que você não precisa salvar arquivos temporários, mas pode manter todos em memória / canais.

Se ./carga_postgres.sh não pode ler a partir de stdin, mas deve ler a partir de um arquivo, você pode salvá-lo em um arquivo:

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

Os trabalhos grandes geralmente falham no meio do caminho. O GNU Parallel pode ajudá-lo, executando novamente os trabalhos com falha:

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

Se isso falhar, você poderá executar novamente o acima. Ele irá ignorar os blocos que já foram processados com sucesso.

    
por 01.03.2013 / 17:35
2

Por que não usar o --pipe AND --pipepart com o GNU Parallel? Isso elimina o gato extra e inicia leituras diretas do arquivo no disco:

parallel --pipe --pipepart -a 2011.psv --block 500M ./carga_postgres.sh
    
por 22.04.2015 / 22:18
0

Uma alternativa para tornar split imprimir os nomes dos arquivos é detectar quando os arquivos estão prontos. No Linux, você pode usar o recurso inotify e, especificamente, o utilitário inotifywait .

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

Você precisará matar inotifywait manualmente. Matá-lo automaticamente é um pouco difícil porque existe uma possível condição de corrida: se você o matar assim que split terminar, ele pode ter recebido eventos que ainda não foram relatados. Para garantir que todos os eventos sejam relatados, conte os arquivos correspondentes.

{
  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)
    
por 28.02.2013 / 22:30
0

Eu achei as respostas postadas aqui como sendo complexas, então perguntei no Stack Overflow e recebi esta resposta:

Se você usar GNU split , poderá fazer isso com o --filter option

‘--filter=command’
With this option, rather than simply writing to each output file, write through a pipe to the specified shell command for each output file. command should use the $FILE environment variable, which is set to a different output file name for each invocation of the command.

Você pode criar um script de shell, que cria um arquivo e começa a carga_postgres.sh no final em segundo plano

#! /bin/sh

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

e use esse script como o filtro

split -l 50000000 --filter=./filter.sh 2011.psv
    
por 01.03.2013 / 07:20