Hadoop:如何在HDFS中压缩文件?

14

我最近在Hadoop中设置了LZO压缩。在HDFS中,最简单的压缩文件的方法是什么?我想要压缩一个文件,然后删除原始文件。我应该创建一个使用LZO压缩的IdentityMapper和IdentityReducer的MR作业吗?

7个回答

21
对我来说,编写一个 Hadoop Streaming 作业来压缩文件的开销较低。
这是我运行的命令:
hadoop jar $HADOOP_HOME/contrib/streaming/hadoop-streaming-0.20.2-cdh3u2.jar \
  -Dmapred.output.compress=true \
  -Dmapred.compress.map.output=true \
  -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec \
  -Dmapred.reduce.tasks=0 \
  -input <input-path> \
  -output $OUTPUT \
  -mapper "cut -f 2"

如果有需要,我通常会将输出存储在临时文件夹中以防出现问题:

OUTPUT=/tmp/hdfs-gzip-`basename $1`-$RANDOM

额外补充一点,我在流处理任务中没有指定一个reducer,但您可以自行指定。这将强制所有行进行排序,对于大文件可能需要很长时间。也许有一种方法可以通过覆盖分区器来避免这个问题,但我没有费心去解决它。不幸的是,您可能最终会得到许多小文件,这些文件不能有效地利用HDFS块。 这就是研究Hadoop Archives的原因之一。


为什么要使用“cut -f 2”而不是“cat”? - dranxo
2
映射器的输入是由制表符分隔的键和值。键是文件中行的字节偏移量,值是该行的文本。cut -f 2仅输出值。 - Jeff Wu
我该如何在HDFS中压缩文件夹? - subhashlg26
1
下面的答案实际上使用了cat命令,这是正确的答案。 - rjurney
以上命令在压缩输出的每行末尾会多出一个“制表符”字符。 - PradeepKumbhar

7

我建议您编写一个MapReduce作业,正如您所说,只使用Identity映射器。在此过程中,您应考虑将数据写入序列文件以提高性能加载速度。您还可以将序列文件存储在块级和记录级压缩中。您应该测试一下哪种方式最适合您的需求,因为两者都针对不同类型的记录进行了优化。


5

Jeff Wu提供的流媒体命令和压缩文件的连接将生成一个单独的压缩文件。当非Java映射器传递给流媒体作业并且输入格式为文本流时,输出仅为值而不是键。

hadoop jar contrib/streaming/hadoop-streaming-1.0.3.jar \
            -Dmapred.reduce.tasks=0 \
            -Dmapred.output.compress=true \
            -Dmapred.compress.map.output=true \
            -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec \
            -input filename \
            -output /filename \
            -mapper /bin/cat \
            -inputformat org.apache.hadoop.mapred.TextInputFormat \
            -outputformat org.apache.hadoop.mapred.TextOutputFormat
hadoop fs -cat /path/part* | hadoop fs -put - /path/compressed.gz

只是想确保我理解这些命令。第一个命令将输出生成到压缩文件中,但实际文件不在*.gz格式中,所以第二个命令是用来重命名的? - nevets1219
不,第一条命令生成了压缩的*.gz part文件(很多个)。而第二条命令是将这些part文件连接在一起成为一个单独的'compressed.gz'文件。 - PradeepKumbhar
以上命令在压缩输出的每行末尾会多出一个“tab”字符。 - PradeepKumbhar

4
这是我使用过的内容:
/*
 * Pig script to compress a directory
 * input:   hdfs input directory to compress
 *          hdfs output directory
 * 
 * 
 */

set output.compression.enabled true;
set output.compression.codec org.apache.hadoop.io.compress.BZip2Codec;

--comma seperated list of hdfs directories to compress
input0 = LOAD '$IN_DIR' USING PigStorage();

--single output directory
STORE input0 INTO '$OUT_DIR' USING PigStorage(); 

尽管它不是LZO,所以可能会慢一些。

这个程序会压缩输入目录中的每个文件,还是将所有文件视为一个大文件进行压缩,然后输出可能少得多的文件?如果是后者,是否有一种方法可以指定Pig尝试一次压缩多少数据,例如每次3GB? - AatG
是的,它将整个输入目录加载到单个别名中,并输出为${OUT_DIR}/part-m-*.bz2。如果您想要一个3Gb的输入目录,则控制IN_DIR。 - dranxo

4

@Chitra 由于声誉问题,我无法发表评论

以下是一条完整的命令:不要使用第二条命令,可以直接将其压缩成一个文件

hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar \
        -Dmapred.reduce.tasks=1 \
        -Dmapred.output.compress=true \
        -Dmapred.compress.map.output=true \
        -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.BZip2Codec \
        -input /input/raw_file \
        -output /archives/ \
        -mapper /bin/cat \
        -reducer /bin/cat \
        -inputformat org.apache.hadoop.mapred.TextInputFormat \
        -outputformat org.apache.hadoop.mapred.TextOutputFormat

因此,只有一个压缩文件能够使你获得很多空间。
例如,假设我有4个10MB的文件(它是纯文本,以JSON格式化)
只有映射时,会给我4个650KB的文件。如果我进行映射和归并,我会得到1个1.05 MB的文件。

2

我知道这是一个旧的帖子,但如果有人像我一样关注这个帖子,那么知道以下两种方法中的任何一种都可以在每行末尾给你一个tab(\t)字符。

 hadoop jar $HADOOP_HOME/contrib/streaming/hadoop-streaming-0.20.2-cdh3u2.jar \
      -Dmapred.output.compress=true \
      -Dmapred.compress.map.output=true \
      -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec \
      -Dmapred.reduce.tasks=0 \
      -input <input-path> \
      -output $OUTPUT \
      -mapper "cut -f 2"


hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar \
        -Dmapred.reduce.tasks=1 \
        -Dmapred.output.compress=true \
        -Dmapred.compress.map.output=true \
        -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.BZip2Codec \
        -input /input/raw_file \
        -output /archives/ \
        -mapper /bin/cat \
        -reducer /bin/cat \
        -inputformat org.apache.hadoop.mapred.TextInputFormat \
        -outputformat org.apache.hadoop.mapred.TextOutputFormat

从这个hadoop-streaming.jar在每行末尾添加x'09'的问题中,我找到了解决方法,我们需要设置以下两个参数来指定你使用的分隔符(在我的情况下是逗号)。
 -Dstream.map.output.field.separator=, \
 -Dmapred.textoutputformat.separator=, \

完整执行命令

hadoop jar <HADOOP_HOME>/jars/hadoop-streaming-2.6.0-cdh5.4.11.jar \
        -Dmapred.reduce.tasks=1 \
        -Dmapred.output.compress=true \
        -Dmapred.compress.map.output=true \
 -Dstream.map.output.field.separator=, \
 -Dmapred.textoutputformat.separator=, \
        -Dmapred.output.compression.codec=org.apache.hadoop.io.compress.Lz4Codec \
        -input file:////home/admin.kopparapu/accenture/File1_PII_Phone_part3.csv \
        -output file:///home/admin.kopparapu/accenture/part3 \
 -mapper /bin/cat \
        -reducer /bin/cat \
        -inputformat org.apache.hadoop.mapred.TextInputFormat \
        -outputformat org.apache.hadoop.mapred.TextOutputFormat

-4

如果你压缩单个文件,可能会节省一些空间,但你无法真正利用Hadoop的能力来处理该文件,因为解压缩必须由单个Map任务按顺序完成。如果你有很多文件,可以使用Hadoop Archive,但我不确定它是否包括任何类型的压缩。我能想到的主要压缩用例是压缩Maps的输出以发送到Reduces(节省网络I/O)。

哦,为了更完整地回答你的问题,你可能需要实现自己的RecordReader和/或InputFormat,以确保整个文件被单个Map任务读取,并且使用正确的解压缩过滤器。


Hadoop已经集成了压缩库,请参见http://www.cloudera.com/blog/2009/06/parallel-lzo-splittable-compression-for-hadoop/。 - schmmd
有趣。我以为你在谈论输入被压缩,而不是压缩输出,抱歉。你是否关心输出文件中数据的排序?如果你不关心输出文件的排序,你可以轻松地使用文件系统API,并将FSDataOutputStream包装在LZO压缩过滤器中。如果你关心排序,则使用FileOutputFormat.setCompressOutput()和setOutputCompressorClass()。它就在Javadoc中,通过Google在10秒内找到了它。 - Drizzt321

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接