结合使用GNU Parallel和Split


9

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

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

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

我已经阅读了split手册页,但找不到任何东西。有没有办法使用split其他工具来做到这一点?

Answers:


13

使用--pipe:

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

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

如果不需要50,000,000行,则--block更快:

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

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

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

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

主要优点是您无需保存临时文件,但可以将所有文件保留在内存/管道中。

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

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是否可以帮助您。
Ole Tange

2

为什么不将--pipe AND --pipepart与GNU Parallel一起使用?这消除了多余的麻烦,并开始从磁盘上的文件直接读取:

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

1

我发现这里发布的答案很复杂,所以我对Stack Overflow进行了询问,得到了以下答案:

如果你使用GNU split,你可以用做--filter选项

'--filter = command'
使用此选项,而不是简单地写入每个输出文件,而是通过管道将每个输出文件写入指定的shell命令。命令应使用$ FILE环境变量,该变量在每次调用命令时都设置为不同的输出文件名。

您可以创建一个shell脚本,该脚本创建一个文件并在后台的末尾启动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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.