Использование GNU Parallel с Split

9

Я загружаю довольно гигантский файл в базу данных postgresql. Для этого я сначала использую splitфайл, чтобы получить файлы меньшего размера (30 ГБ каждый), а затем загружаю каждый файл меньшего размера в базу данных, используя GNU Parallelи psql copy.

Проблема в том, что для разделения файла требуется около 7 часов, а затем он начинает загружать файл на ядро. Мне нужен способ заставить splitимя файла выводить на вывод std каждый раз, когда он заканчивает запись файла, поэтому я могу передать его по конвейеру, Parallelи он начинает загружать файлы в тот момент, когда он splitзаканчивает писать. Что-то вроде этого:

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

Я прочитал splitсправочные страницы и ничего не могу найти. Есть ли способ сделать это с помощью splitкакого-либо другого инструмента?

топографический
источник

Ответы:

13

Используйте --pipe:

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

Это требует ./carga_postgres.sh для чтения из стандартного ввода, а не из файла, и медленно для версии GNU Parallel <20130222.

Если вам не нужно ровно 50000000 строк, --block быстрее:

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

Это передаст фрагменты размером около 500 МБ \ n.

Я не знаю, что ./carga_postgres.sh содержит, но я предполагаю, что он содержит psql с паролем имени пользователя. В этом случае вы можете использовать GNU SQL (который является частью GNU Parallel):

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

Основным преимуществом является то, что вам не нужно сохранять временные файлы, но вы можете хранить все в памяти / каналах.

Если ./carga_postgres.sh не может читать из стандартного ввода, но должен читать из файла, вы можете сохранить его в файл:

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

Крупные рабочие места часто терпят неудачу на полпути. GNU Parallel может помочь вам, повторно запустив сбойные задания:

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

Если это не помогло, вы можете снова запустить выше. Он пропустит блоки, которые уже успешно обработаны.

Оле Танге
источник
1
Если у вас более новая версия GNU Parallel> 20140422, используйте ответ @ RobertB с --pipepart. Если это не сработает, посмотрите, могут ли помочь --fifo или --cat.
Оле Танге
2

Почему бы не использовать --pipe AND --pipepart с GNU Parallel? Это устраняет лишнюю кошку и запускает прямое чтение из файла на диске:

parallel --pipe --pipepart -a 2011.psv --block 500M ./carga_postgres.sh
Роберт Б.
источник
1

Я нашел ответы, размещенные здесь, чтобы быть сложным, поэтому я спросил о переполнении стека, и я получил этот ответ:

Если вы используете GNU split, вы можете сделать это с --filterопцией

'--filter = command'
С помощью этой опции вместо простой записи в каждый выходной файл пишите через канал в указанную команду оболочки для каждого выходного файла. команда должна использовать переменную среды $ FILE, для которой задано другое имя выходного файла для каждого вызова команды.

Вы можете создать скрипт оболочки, который создаст файл и запустить carga_postgres.sh в конце в фоновом режиме.

#! /bin/sh

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

и использовать этот скрипт в качестве фильтра

split -l 50000000 --filter=./filter.sh 2011.psv
топографический
источник
0

Альтернативой splitпечати имен файлов является определение того, когда файлы готовы. В Linux вы можете использовать средство inotify и, в частности, inotifywaitутилиту.

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

Вам нужно будет убить inotifywaitвручную. Убить его автоматически немного сложно, потому что есть потенциальное состояние гонки: если вы убьете его, как только splitзакончите, возможно, он получил события, о которых он еще не сообщил. Чтобы убедиться, что все события сообщаются, подсчитайте соответствующие файлы.

{
  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)
Жиль "ТАК - перестань быть злым"
источник