[hadoop源码阅读][4]-org.apache.hadoop.io.compress系列3-使用压缩
hadoop中支持的压缩方式有多种,比如Gzip,bzip2,zlib等,其中Gzip是hadoop中内置就支持的一种压缩方式,这种压缩方式在平时linux的开发人员和管理员中使用的比较广泛,压缩比也比较高,压缩速度也还不错,所以很多人都喜欢第一趋向于使用这种压缩格式进行文件的压缩。
在hadoop中,要在mapreduce 的job中使用gzip压缩是比较容易的,不记得是从哪个版本开始,hadoop就内置了使用gzip压缩格式读取输入文件,写中间结果和输出结果的支持。
1.从压缩的输入文件时直接读入
由于hadoop在读取输入文件时,会很智能的根据输入文件的后缀名来进行判断是否采用压缩格式进行读入,所以当读到输入文件是***.gz时,就会猜测该文件是一个用gzip压缩过的文件,于是就会尝试使用gzip的读取方式来读取.
public CompressionCodecFactory(Configuration conf) { codecs = new TreeMap<String, CompressionCodec>(); List<Class<? extends CompressionCodec>> codecClasses = getCodecClasses(conf);//conf.get("io.compression.codecs");从这个配置里面取得配置的解码器 if (codecClasses == null) { addCodec(new GzipCodec());//如果core-site.xml里面没有配置的话 就是有默认的这2个 addCodec(new DefaultCodec()); } else { Iterator<Class<? extends CompressionCodec>> itr = codecClasses.iterator(); while (itr.hasNext()) { CompressionCodec codec = ReflectionUtils.newInstance(itr.next(), conf); addCodec(codec); } } }
如果使用其他的压缩方式可以这样来配置在core-site.xml中
<property> <name>io.compression.codecs</name> <value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec</value> </property>
或者在代码中
conf.set("io.compression.codecs","org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.GzipCodec,com.hadoop.compression.lzo.LzopCodec");
在默认的inputformat或者outputformat里面,都自带编解码的检测代码,如果自己实现format的话,可能需要自己添加如下代码
实现inputformat
CompressionCodecFactory compressionCodecs = new CompressionCodecFactory(job); final CompressionCodec codec = compressionCodecs.getCodec(file); CompressionInputStreamin=codec.createInputStream(fileIn); ....
Class <? extends CompressionCodec > codecClass =getOutputCompressorClass(job, GzipCodec.class); CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, job); Path file =FileOutputFormat.getTaskOutputPath(job,name + codec.getDefaultExtension()); FileSystem fs = file.getFileSystem(job); FSDataOutputStream fileOut = fs.create(file, progress); CompressionOutputStreamout=codec.createOutputStream(fileOut)) ....
2.将mapreduce job所产生的中间结果进行压缩
由于mapreduce算法本身的特征,必然会在job的运行过程中产生一定的中间结果文件,当数据量 很大的时候,这些中间结果也非常的客观,一定程度上,对job的效率会有一定的影响。由于通常计算任务的瓶颈都在磁盘的读写IO上,因此如果能够减少因中 间文件而产生的disk IO,则对作业的效率肯定有益无害。因此如果希望将mapreduce作业的中间结果进行压缩,在hadoop的conf(可以通过修改hadoop- site.xml的配置选项,可以在程序中用JobConf的类接口设置,或者在提交作业时用-D选项来设置该选项)中配置一个选项:
<property> <name>mapred.compress.map.output</name> <value>true</value> </property>
conf.setCompressMapOutput(true); conf.setMapOutputCompressorClass(GzipCodec.class);
这样,作业就会将产生的中间结果写入slave local的时候,对结果进行压缩,reduce读取时也能够根据gz的后缀知道该中间结果是压缩文件,于是采用相应的读取格式来读取。
3.将最终的计算输出的结果进行压缩
有的时候,我们需要对作业的运行结果进行历史保存,但是如果每天积累的计算结果非常大,又想要保存尽量多的历史结果一边将来的结算,那么日积月累下,就会占据非常非常大的HDFS的存储空间, 并且由于是历史数据,使用的频率也不高,这就会造成很大的存储空间的浪费,因此,将计算的结果进行压缩,也是一种非常好的节省空间的方法。要在 hadoop job中做到这一点也很容易,只需要告诉hadoop,“我想要多job的输出结果进行压缩,并保存到HDFS上去”,就行了。具体的操作就是:在 conf中设置配置选项:
<property> <name>mapred.output.compress</name> <value>true</value> </property>
conf.setBoolean("mapred.output.compress", true); conf.setClass("mapred.output.compression.codec", GzipCodec.class,CompressionCodec.class);
4.是使用hadoop-0.19.1对一个任务进行三种方式的压缩的对比:
-
读取非压缩文件,中间结果不压缩,输出结果也不压缩
- 读取压缩文件,中间结果不压缩,输出结果不压缩
- 读取非压缩文件,中间结果压缩,输出结果不压缩
- 读取非压缩文件,中间结果不压缩,输出结果压缩
因此我们可以看到,在hadoop中使用gzip压缩来进行读取,中间结果,数据结果的保存都是非常的容易,因为hadoop native本身就提供了我们相应的类来解析和压缩数据。不过这里要特别提到的是:gzip压缩格式在hadoop中的支持有一定的局限性: 由于gzip压缩算法本身的原因,我们无法对gzip压缩文件进行分块,也就是说,在hadoop中,如果想要用hadoop 的mapreduce来处理数据,没一个mapper就必须对应一个gz文件,不能多个mapper对一个gzip文件的多个chunk进行并行的处理, 因此如果要在hadoop mapreduce任务中使用gzip,在数据处理之前就需要对数据进行认为的切分,让一个mapper来处理一块数据。这样其实有一点有违 mapreduce的本质。