MapReduce处理数据时,需要考虑压缩格式是否支持分片是很重要的。考虑存储在HDFS重的未压缩文件,其大小为1GB。HDFS块大小为128M,该文件在HDFS存储分成8块存,作为MapReduce输入将创建8个分片(split,也称为“分块”)。每个分片都被作为一个独立的MapTask的输入单独进行处理。
假设,一个压缩后1GB的gzip格式的文件。HDFS块大小为128M,在HDFS存储8个分块。然而针对每一block创建一个分片是没有用的,因为不能从gzip格式数据流中的任意一点开始读取。因此gzip格式不支持分片(分割)。
对于格式为gzip格式的1GB的文件,随被分成8块存储,但是MapReduce对gzip格式文件不分片,也就是说8块存储的数据通过一个MapTask处理,大都不是map的本地数据。因为MapTask少,从而导致运行时间变长。
对于gzip2格式压缩1GB文件,因为它支持分片,故可以多个MapTask处理,性能较高。
思考问题:实际项目应用中应该使用哪种压缩格式呢?
考虑Hadoop应用处理的数据集比较大,因此需要借助压缩。按照效率从高到低排列的
(1) 使用容器格式文件,例如:顺序文件、RCFile、Avro数据格式支持压缩和切分文件。另外在配合使用一些快速压缩工具,例如:LZO、LZ4或者Snappy.
(2) 使用支持切分压缩格式,例如gzip2
(3) 在应用中将文件切分成块,对每块进行任意格式压缩。这种情况确保压缩后的数据库接近HDFS块大小。
(4) 存储未压缩文件,以原始文件存储。
总之,对于大文件来说,不要使用不支持切分整个文件的压缩格式,因为失去数据本地特性,进而造成MapReduce应用效率低下。
编写MapReduce程序时候,有时候需要在程序中对map任务的输出结果或者MapReduce作业进行压缩。
(1)对map任务输出进行压缩
由于map任务的输出需要写到磁盘并通过网络传输到reducer节点,所以如果使用LZO、LZ4或者Snappy这样的快速压缩方式和,是可以提升性能的。启用map任务中间节点的方法:
// 对map任务输出进行压缩 conf.setBoolean("mapreduce.map.output.compress.codec", true); conf.setClass("mapreduce.map.output.compress", GzipCodec.class, CompressionCodec.class); |
(2)Map作业中使用压缩
// 对作业输出结果进行压缩 FileOutputFormat.setCompressOutput(job, true); FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class); |
如果输出生成顺序文件(sequence file),可以通过以下设置来完成
job.setOutputFormatClass(SequenceFileOutputFormat.class); SequenceFileOutputFormat.setCompressOutput(job, true); SequenceFileOutputFormat.setOutputCompressorClass(job, GzipCodec.class); SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK); |
注意:默认情况下是RECORD,即针对每条记录进行压缩。如果改成BLOCK,将针对一组记录进行压缩。这也是推荐的压缩策略,因此它的压缩效率更高。