Menggunakan GNU Parallel With Split


9

Saya memuat file yang sangat besar ke database postgresql. Untuk melakukan ini saya pertama kali menggunakan splitdalam file untuk mendapatkan file yang lebih kecil (masing-masing 30Gb) dan kemudian saya memuat setiap file yang lebih kecil ke database menggunakan GNU Paralleldan psql copy.

Masalahnya adalah bahwa dibutuhkan sekitar 7 jam untuk membagi file, dan kemudian mulai memuat file per inti. Yang saya butuhkan adalah cara untuk memberi tahu splituntuk mencetak nama file ke std output setiap kali selesai menulis file sehingga saya dapat mengirimnya ke pipa Paralleldan mulai memuat file pada saat splitselesai menulisnya. Sesuatu seperti ini:

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

Saya telah membaca splithalaman manual dan saya tidak dapat menemukan apa pun. Apakah ada cara untuk melakukan ini dengan splitatau alat lain?

Jawaban:


13

Gunakan --pipe:

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

Dibutuhkan ./carga_postgres.sh untuk membaca dari stdin dan bukan dari file, dan lambat untuk GNU Parallel versi <20130222.

Jika Anda tidak membutuhkan tepat 50000000 baris, --block lebih cepat:

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

Ini akan memberikan potongan sekitar 500MB yang dibagi pada \ n.

Saya tidak tahu apa yang mengandung ./carga_postgres.sh, tapi dugaan saya, ini berisi psql dengan kata sandi nama pengguna. Dalam hal ini Anda mungkin ingin menggunakan GNU SQL (yang merupakan bagian dari GNU Parallel):

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

Manfaat utama adalah bahwa Anda tidak perlu menyimpan file sementara, tetapi dapat menyimpan semua dalam memori / pipa.

Jika ./carga_postgres.sh tidak dapat membaca dari stdin, tetapi harus membaca dari file, Anda dapat menyimpannya di file:

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

Pekerjaan besar sering gagal setengah jalan. GNU Parallel dapat membantu Anda dengan menjalankan kembali pekerjaan yang gagal:

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

Jika ini gagal maka Anda dapat menjalankan kembali di atas. Ini akan melewati blok yang sudah berhasil diproses.


1
Jika Anda memiliki versi GNU Parallel> 20140422 yang lebih baru gunakan jawaban @ RobertB dengan --pepepart. Jika itu tidak berhasil, lihat apakah --fifo atau --cat dapat membantu Anda.
Ole Tange

2

Mengapa tidak menggunakan --pipe DAN --pipepart dengan GNU Parallel? Ini menghilangkan kucing ekstra dan mulai membaca langsung dari file pada disk:

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

1

Saya menemukan jawaban yang diposting di sini adalah cara yang kompleks, jadi saya bertanya pada Stack Overflow dan saya mendapat jawaban ini :

Jika Anda menggunakan GNU split, Anda dapat melakukan ini dengan --filteropsi

'--filter = command'
Dengan opsi ini, daripada hanya menulis ke setiap file output, tulis melalui pipa ke perintah shell yang ditentukan untuk setiap file output. perintah harus menggunakan variabel lingkungan $ FILE, yang diatur ke nama file output berbeda untuk setiap doa perintah.

Anda dapat membuat skrip shell, yang membuat file dan mulai carga_postgres.sh di akhir di latar belakang

#! /bin/sh

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

dan gunakan skrip itu sebagai filter

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

0

Alternatif untuk membuat splitmencetak nama file adalah dengan mendeteksi kapan file siap. Di Linux, Anda dapat menggunakan fasilitas inotify , dan khususnya inotifywaitutilitas.

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

Anda harus membunuh inotifywaitsecara manual. Membunuh secara otomatis agak sulit karena ada kondisi lomba yang potensial: jika Anda membunuhnya segera setelah splitselesai, ia mungkin telah menerima peristiwa yang belum dilaporkan. Untuk memastikan bahwa semua acara dilaporkan, hitung file yang cocok.

{
  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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.