使用 GNU Parallel 和 Split

Top*_*opo 9 split files gnu-parallel

我正在将一个非常庞大的文件加载到 postgresql 数据库中。为此,我首先split在文件中使用获取较小的文件(每个 30Gb),然后使用GNU Parallel和将每个较小的文件加载到数据库中psql copy

问题是分割文件大约需要7个小时,然后它开始每个核心加载一个文件。我需要的是一种方法来告诉split它每次完成写入文件时将文件名打印到 std 输出,以便我可以将其通过管道传输到Parallel它并在split完成写入时开始加载文件。像这样的东西:

split -l 50000000 2011.psv carga/2011_ | parallel ./carga_postgres.sh {}
Run Code Online (Sandbox Code Playgroud)

我已阅读split手册页,但找不到任何内容。有没有办法用split或任何其他工具来做到这一点?

Ole*_*nge 13

使用 --pipe:

cat 2011.psv | parallel --pipe -l 50000000 ./carga_postgres.sh
Run Code Online (Sandbox Code Playgroud)

它需要 ./carga_postgres.sh 从 stdin 而不是从文件中读取,并且对于 GNU Parallel 版本 < 20130222 来说很慢。

如果您不需要正好 50000000 行,则 --block 会更快:

cat 2011.psv | parallel --pipe --block 500M ./carga_postgres.sh
Run Code Online (Sandbox Code Playgroud)

这将在 \n 上传递大约 500MB 的块。

我不知道 ./carga_postgres.sh 包含什么,但我猜它包含带有用户名密码的 psql。在这种情况下,您可能想要使用 GNU SQL(它是 GNU Parallel 的一部分):

cat 2011.psv | parallel --pipe --block 500M sql pg://user:pass@host/db
Run Code Online (Sandbox Code Playgroud)

主要好处是您不需要保存临时文件,但可以将所有文件保存在内存/管道中。

如果 ./carga_postgres.sh 无法从 stdin 读取,但必须从文件读取,则可以将其保存到文件中:

cat 2011.psv | parallel --pipe --block 500M "cat > {#}; ./carga_postgres.sh {#}"
Run Code Online (Sandbox Code Playgroud)

大型作业通常会在中途失败。GNU Parallel 可以通过重新运行失败的作业来帮助您:

cat 2011.psv | parallel --pipe --block 500M --joblog my_log --resume-failed "cat > {#}; ./carga_postgres.sh {#}"
Run Code Online (Sandbox Code Playgroud)

如果失败,那么你可以重新运行上面的。它将跳过已经成功处理的块。


Gil*_*il' 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_
Run Code Online (Sandbox Code Playgroud)

你需要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)
Run Code Online (Sandbox Code Playgroud)