Używanie GNU Parallel z Splitem


9

Ładuję dość gigantyczny plik do bazy danych postgresql. Aby to zrobić, najpierw używam splitw pliku, aby uzyskać mniejsze pliki (30 Gb każdy), a następnie ładuję każdy mniejszy plik do bazy danych za pomocą GNU Paralleli psql copy.

Problem polega na tym, że dzielenie pliku zajmuje około 7 godzin, a następnie zaczyna ładować plik na rdzeń. Potrzebuję sposobu, aby powiedzieć, splitaby wydrukować nazwę pliku na standardowym wyjściu za każdym razem, gdy kończy zapisywanie pliku, dzięki czemu mogę go przesłać do Parallelniego, a on zaczyna ładować pliki w momencie, splitgdy go zapisuje. Coś takiego:

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

Przeczytałem splitstrony podręcznika i nic nie mogę znaleźć. Czy można to zrobić za pomocą splitinnego narzędzia?

Odpowiedzi:


13

Użyj --pipe:

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

Wymaga ./carga_postgres.sh do odczytu ze stdin, a nie z pliku, i jest powolny w przypadku GNU Parallel w wersji <20130222.

Jeśli nie potrzebujesz dokładnie 50000000 linii, --block jest szybszy:

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

Spowoduje to przekazanie fragmentów o podzieleniu około 500 MB na \ n.

Nie wiem, co zawiera plik ./carga_postgres.sh, ale domyślam się, że zawiera psql z hasłem nazwy użytkownika. W takim przypadku możesz użyć GNU SQL (który jest częścią GNU Parallel):

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

Główną zaletą jest to, że nie trzeba zapisywać plików tymczasowych, ale można przechowywać wszystko w pamięci / potokach.

Jeśli ./carga_postgres.sh nie może czytać ze standardowego wejścia, ale musi czytać z pliku, możesz zapisać go do pliku:

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

Duże prace często kończą się niepowodzeniem. GNU Parallel może ci pomóc poprzez ponowne uruchomienie nieudanych zadań:

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

Jeśli to się nie powiedzie, możesz ponownie uruchomić powyższe. Pominie bloki, które zostały już pomyślnie przetworzone.


1
Jeśli masz nowszą wersję GNU Parallel> 20140422, użyj odpowiedzi @ RobertB z opcją --pipepart. Jeśli to nie działa bezpośrednio, sprawdź, czy --fifo lub --cat może ci pomóc.
Ole Tange

2

Dlaczego nie użyć --pipe AND --pipepart z GNU Parallel? To eliminuje dodatkowy kot i rozpoczyna bezpośrednie odczytywanie z pliku na dysku:

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

1

Znalazłem zamieszczone tutaj odpowiedzi, które są skomplikowane, więc zapytałem na Stack Overflow i otrzymałem odpowiedź:

Jeśli używasz GNU split, możesz to zrobić z --filteropcją

„--filter = polecenie”
Dzięki tej opcji, zamiast po prostu zapisywać do każdego pliku wyjściowego, pisz przez potok do określonego polecenia powłoki dla każdego pliku wyjściowego. polecenie powinno używać zmiennej środowiskowej $ FILE, która jest ustawiona na inną nazwę pliku wyjściowego dla każdego wywołania polecenia.

Możesz utworzyć skrypt powłoki, który tworzy plik i uruchomić carga_postgres.sh na końcu w tle

#! /bin/sh

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

i użyj tego skryptu jako filtra

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

0

Alternatywą dla splitdrukowania nazw plików jest wykrycie, kiedy pliki są gotowe. W systemie Linux można użyć funkcji inotify , a zwłaszcza inotifywaitnarzędzia.

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

Musisz zabić inotifywaitręcznie. Automatyczne zabicie go jest trochę trudne, ponieważ istnieje potencjalny warunek wyścigu: jeśli zabijesz go zaraz po splitzakończeniu, może otrzymać zdarzenia, których jeszcze nie zgłosił. Aby upewnić się, że wszystkie zdarzenia są zgłaszane, policz pasujące pliki.

{
  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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.