多核 gzip 解压缩,将输出文件 (csv) 拆分为 1Gb/文件

Multicore gzip uncompression with spliting output file (csv) to parts by 1Gb/file

我有 10Gb gzip 存档(未压缩大约 60Gb)。

有没有办法通过多线程解压缩此文件 + 以 1Gb/部分(n-lines/part、maybe)即时将输出拆分为多个部分?

如果我这样做:

pigz -dc 60GB.csv.gz | dd bs=8M skip=0 count=512 of=4G-part-1.csv

我可以得到一个 4Gb 的文件,但它不关心总是从下一行开始,所以我的文件中的行不会正确结束。

此外,正如我所注意到的,我的带有永久磁盘的 GCE 实例最大块大小为 33kb,因此我实际上不能使用上面的命令,但必须打印如下内容:

pigz -dc 60GB.csv.gz | dd bs=1024 skip=0 count=4194304 of=4G-part-1.csv

pigz -dc 60GB.csv.gz | dd bs=1024 skip=4194304 count=4194304 of=4G-part-2.csv

pigz -dc 60GB.csv.gz | dd bs=1024 skip=$((4194304*2)) count=4194304 of=4G-part-3.csv

所以,我必须采取一些技巧来始终从新行开始文件..

更新:

zcat 60GB.csv.gz |awk 'NR%43000000==1{x="part-"++i".csv";}{print > x}'

成功了。

除非专门为这样的操作做准备,或者除非为此目的建立了索引,否则不会。 gzip 格式本质上要求在流中的任何点之前解压缩数据,以便在流中的该点之后解压缩数据。所以不能并行化。

出路是 a) 使用同步点重新压缩 gzip 文件并保存这些位置,或者 b) 遍历整个 gzip 文件一次并在这些点处使用先前的上下文创建另一个入口点文件。

对于 a),zlib 提供 Z_FULL_FLUSH 操作,可在流中插入同步点,您可以从中开始解压,而无需以前的历史记录。您可能希望谨慎地创建此类点,因为它们会降低压缩率。

对于 b),zran.c 提供了一个示例,说明如何将索引构建到 gzip 文件中。您需要按顺序通过流一次来构建索引,但是这样做之后,您可以在保存的位置开始解压缩。

根据您在问题中提到的尺寸,您似乎获得了大约 6 比 1 的压缩率。这似乎不太适合文本,但无论如何...

正如 Mark 所说,您不能只是将中流浸入 gz 文件并期望着陆在一个新行上。您的 dd 选项将不起作用,因为 dd 仅复制字节,它不会检测压缩的换行符。如果索引超出范围,以下命令行解决方案可能会有所帮助:

$ gzcat 60GB.csv.gz | awk -v n=1 '!NR%20000000{n++} {print|("gzip>part-"n".gz")}'

这会解压缩您的文件,以便我们计算行数,然后处理流,每 20000000 行更改一次输出文件名。您可以在上面代码中看到 "gzip" 的地方调整重新压缩选项。

如果您不想压缩输出,可以简化该行的最后一部分:

$ gzcat 60GB.csv.gz | awk -v n=1 '!NR%3500000{n++} {print>("part-"n".csv")}'

您可能需要调整行数以获得接近您目标的文件大小。

请注意,如果您的 shell 是 csh/tcsh,您可能必须在 awk 脚本中转义感叹号以避免它被解释为历史参考。

更新:

如果您想了解脚本正在执行的操作的状态,awk 可以做到。像这样的东西可能很有趣:

$ gzcat 60GB.csv.gz | awk -v n=1 '!NR%3500000{n++} !NR%1000{printf("part=%d / line=%d\r",n,NR)} {print>("part-"n".csv")}'

这应该每千行显示当前部分和行号。