我正在尝试处理一个相当大的制表符分隔文件(约 30GB)。我可以运行 awk 来重新排列列,但它只使用我的 8 核机器的一个核心。我怎样才能使用所有核心?(并大大减少我的处理时间)而不物理地分割文件并占用更多磁盘空间?
cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged.tsv
我想做这样的事情,但又不想占用更多的磁盘空间:
split -l 50000 original.tsv sp
for i in $(dir sp*); do ./process_file.sh $i & done;
其中 process_file.sh 本质上就是上面的 cut/awk 语句。
再次强调,这里的关键问题是在不使用另外 30GB 的情况下完成此过程!有什么建议吗?
答案1
由于您使用“split”按行数分隔文件,然后分别处理它们并输出到不同的文件(我猜),您可以执行几个“awk”命令,每个命令仅根据行号处理文件的一部分:
$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; (NR < 50000){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged01.tsv
$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; ((NR >= 50000) && (NR < 100000)){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged02.tsv
$ cut -f3,4,5,6 original.tsv | awk 'BEGIN { FS="\t" }; ((NR >= 100000) && (NR < 150000)){ print $2"\t"$1"\t"$3"\t"$4 }' > abridged03.tsv
NR 是“awk”的内部变量,包含当前行号。每个命令只会处理其范围内的行。但,它们也会经过其他线路,因为它们需要计数。我敢肯定这对你没有帮助,因为你很可能会陷入 IO 瓶颈。但你会有多个进程,允许你使用多个 CPU,如果这是你想要的。;-)
现在,如果如果你的所有行都具有相同的字节长度,那么你肯定可以进行真正的并行化。在这种情况下,你将使用“dd”提取每个“awk”进程的精确部分。你可以做类似的事情:
dd if=original.tsv bs=30 count=50000 skip=0 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged01.tsv
dd if=original.tsv bs=30 count=50000 skip=50000 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged02.tsv
dd if=original.tsv bs=30 count=50000 skip=100000 | cut -f3,4,5,6 | awk 'BEGIN { FS="\t" }; { print $2"\t"$1"\t"$3"\t"$4 }' > abridged03.tsv
其中 30 是每行的字节数。如果您的行的字节数不完全相同(这是最有可能的)但如果您知道行块开始和结束的确切字节,您仍然可以使用 dd 来执行此操作。研究其参数。最后,如果您不知道块的开始和结束位置,可以使用额外的 awk 命令找出它们。但这会在您的文件中增加额外的完整读取。除非您将以不同的方式多次处理原始 .tsv 文件,否则您肯定会花费更多时间进行预处理(计算行块开始和结束的字节),然后进行处理(这可能会有一点好处,因为您肯定会遇到 IO 瓶颈),而不是简单地使用您已知的解决方案。
无论如何,现在您已经有了信息和选择。;-) 祝你好运!(y)
答案2
Fwiw,该split
命令有一个选项,可以在将数据写入输出文件之前对其进行处理。因此,您可以有一个包含转换的--filter=./myscript.sh
预处理脚本,例如,./myscript.sh
cut ... | awk '...' > $FILE
其中是(例如,...)$FILE
生成的输出文件。或者,为了避免所有临时文件,只需连接到单个文件,split
xaa
cut ... | awk '...' >> abridged.tsv
然而,没有理由相信这会比不使用更快split
。
考虑到您正在进行“大数据”处理(以及这个问题最初提出后多少年),将这个 30GB 文件复制到 hdfs(或者,更确切地说,将数据最初放在那里并保存)并使用 apache hive 根据需要选择/格式化数据可能同样容易。