Utilisation de GNU Parallel With Split

9

Je charge un fichier assez gigantesque dans une base de données postgresql. Pour ce faire, j'utilise d'abord splitle fichier pour obtenir des fichiers plus petits (30 Go chacun), puis je charge chaque fichier plus petit dans la base de données à l'aide de GNU Parallelet psql copy.

Le problème est qu'il faut environ 7 heures pour diviser le fichier, puis il commence à charger un fichier par cœur. Ce dont j'ai besoin est un moyen de dire splitd'imprimer le nom du fichier sur la sortie std chaque fois qu'il termine l'écriture d'un fichier afin que je puisse le diriger Parallelet qu'il commence à charger les fichiers au moment où il a splitfini de l'écrire. Quelque chose comme ça:

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

J'ai lu les splitpages de manuel et je ne trouve rien. Existe-t-il un moyen de le faire avec splitou tout autre outil?

Topo
la source

Réponses:

13

Utilisez --pipe:

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

Il nécessite ./carga_postgres.sh pour lire à partir de stdin et non à partir d'un fichier, et est lent pour la version GNU Parallel <20130222.

Si vous n'avez pas besoin exactement de 50000000 lignes, le --block est plus rapide:

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

Cela passera des morceaux d'environ 500 Mo répartis sur \ n.

Je ne sais pas ce que contient ./carga_postgres.sh, mais je pense qu'il contient psql avec le mot de passe du nom d'utilisateur. Dans ce cas, vous voudrez peut-être utiliser GNU SQL (qui fait partie de GNU Parallel):

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

Le principal avantage est que vous n'avez pas besoin d'enregistrer les fichiers temporaires, mais que vous pouvez tout garder en mémoire / canaux.

Si ./carga_postgres.sh ne peut pas lire depuis stdin, mais doit lire depuis un fichier, vous pouvez l'enregistrer dans un fichier:

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

Les gros travaux échouent souvent à mi-chemin. GNU Parallel peut vous aider en réexécutant les tâches ayant échoué:

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

Si cela échoue, vous pouvez réexécuter ce qui précède. Il sautera les blocs déjà traités avec succès.

Ole Tange
la source
1
Si vous avez une version plus récente de GNU Parallel> 20140422, utilisez la réponse de @ RobertB avec --pipepart. Si cela ne fonctionne pas directement, voyez si --fifo ou --cat peut vous aider.
Ole Tange
2

Pourquoi ne pas utiliser --pipe ET --pipepart avec GNU Parallel? Cela élimine le chat supplémentaire et démarre les lectures directes à partir du fichier sur le disque:

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

J'ai trouvé que les réponses publiées ici étaient trop complexes, alors j'ai demandé sur Stack Overflow et j'ai obtenu cette réponse:

Si vous utilisez GNU split, vous pouvez le faire avec l' --filteroption

'--filter = command'
Avec cette option, plutôt que d'écrire simplement dans chaque fichier de sortie, écrivez via un canal dans la commande shell spécifiée pour chaque fichier de sortie. La commande doit utiliser la variable d'environnement $ FILE, qui est définie sur un nom de fichier de sortie différent pour chaque appel de la commande.

Vous pouvez créer un script shell, qui crée un fichier et lancez carga_postgres.sh à la fin en arrière-plan

#! /bin/sh

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

et utiliser ce script comme filtre

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

Une alternative à l' splitimpression des noms de fichiers est de détecter quand les fichiers sont prêts. Sous Linux, vous pouvez utiliser la fonction inotify , et en particulier l' inotifywaitutilitaire.

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

Vous devrez tuer inotifywaitmanuellement. Le tuer automatiquement est un peu difficile car il existe une condition de course potentielle: si vous le tuez dès la splitfin, il peut avoir reçu des événements qu'il n'a pas encore signalés. Pour vous assurer que tous les événements sont signalés, comptez les fichiers correspondants.

{
  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- arrête d'être méchant'
la source