文章目录
一、概述
压缩技术能够有效减少底层存储系统(HDFS
)读写字节数。压缩提高了网络带宽和磁盘空间的效率。在运行MR
程序时,I/O
操作、网络数据传输、 Shuffle
和Merge
要花大量的时间,尤其是数据规模很大和工作负载密集的情况下,因此,使用数据压缩显得非常重要。
鉴于磁盘I/O
和网络带宽是Hadoop
的宝贵资源,数据压缩对于节省资源、最小化磁盘I/O
和网络传输非常有帮助。可以在任意MapReduce
阶段启用压缩。不过,尽管压缩与解压操作的CPU
开销不高,其性能的提升和资源的节省并非没有代价。
压缩是提高Hadoop
运行效率的一种优化策略,通过对Mapper
、Reducer
运行过程的数据进行压缩,以减少磁盘IO
,提高MR
程序运行速度。注意:采用压缩技术减少了磁盘IO
,但同时增加了CPU
运算负担,所以,压缩特性运用得当能提高性能,但运用不得当也可能降低性能。
压缩的基本原则:
- 运算密集型的
job
,少用压缩; IO
密集型的job
,多用压缩。
二、MR支持的压缩编码
MR支持的压缩编码:
压缩格式 | 是否为Hadoop自带 | 算法 | 文件扩展名 | 是否可切分 | 换成压缩格式后,原来的程序是否需要修改 |
---|---|---|---|---|---|
DEFLATE | 是,直接使用 | DEFLATE | .deflate | 否 | 和文本处理一样,不需要修改 |
Gzip | 是,直接使用 | DEFLATE | .gz | 否 | 和文本处理一样,不需要修改 |
bzip2 | 是,直接使用 | bzip2 | .bz2 | 是 | 和文本处理一样,不需要修改 |
LZO | 否,需要安装 | LZO | .lzo | 是 | 需要建索引,并指定输入格式 |
Snappy | 否,需要安装 | Snappy | .snappy | 否 | 和文本处理一样,不需要修改 |
Hadoop引入的编码/解码器:
压缩格式 | 对应的编码/解码器 |
---|---|
DEFLATE | org.apache.hadoop.io.compress.DefaultCodec |
Gzip | org.apache.hadoop.io.compress.GzipCodec |
bzip2 | org.apache.hadoop.io.compress.BZip2Codec |
LZO | com.hadoop.compression.lzo.LzopCodec |
Snappy | org.apache.hadoop.io.compress.SnappyCodec |
压缩性能的比较:
压缩算法 | 原始文件大小 | 压缩文件大小 | 压缩速度 | 解压速度 |
---|---|---|---|---|
gzip | 8.3GB | 1.8GB | 17.5MB/s | 58MB/s |
bzip2 | 8.3GB | 1.1GB | 2.4MB/s | 9.5MB/s |
LZO | 8.3GB | 2.9GB | 49.3MB/s | 74.6MB/s |
三、压缩方式选择
(一)Gzip压缩
- **优点:**压缩率比较高,而且压缩/解压速度也比较快;
Hadoop
本身支持,在应用中处理Gzip
格式的文件就和直接处理文本一样;大部分Linux
系统都自带Gzip
命令,使用方便。 - **缺点:**不支持
Split
。 - **应用场景:**当每个文件压缩之后在
130M
以内的(1个块大小内),都可以考虑用Gzip
压缩格式。例如说一天或者一个小时的日志压缩成一个Gzip
文件。
(二)Bzip2压缩
- **优点:**支持
Split
;具有很高的压缩率,比Gzip
压缩率都高;Hadoop
本身自带,使用方便。 - **缺点:**压缩/解压速度慢。
- **应用场景:**适合对速度要求不高,但需要较高的压缩率的时候;或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持
Split
,而且兼容之前的应用程序的情况。
(三)Lzo压缩
- **优点:**压缩/解压速度也比较快,合理的压缩率;支持
Split
,是Hadoop
中最流行的压缩格式;可以在Linux
系统下安装lzop
命令,使用方便。 - **缺点:**压缩率比
Gzip
要低一些;Hadoop
本身不支持,需要安装;在应用中对Lzo
格式的文件需要做一些特殊处理(为了支持Split
需要建索引,还需要指定InputFormat
为Lzo
格式)。 - **应用场景:**一个很大的文本文件,压缩之后还大于
200M
以上的可以考虑,而且单个文件越大,Lzo
优点越越明显。
(四)Snappy压缩
- **优点:**高速压缩速度和合理的压缩率。
- **缺点:**不支持
Split
;压缩率比Gzip
要低;Hadoop
本身不支持,需要安装。 - **应用场景:**当
MapReduce
作业的Map
输出的数据比较大的时候,作为Map
到Reduce
的中间数据的压缩格式;或者作为一个MapReduce
作业的输出和另外一个MapReduce
作业的输入。
四、压缩位置选择
压缩可以在MapReduce作用的任意阶段启用:
-
输入端采用压缩:
在有大量数据并计划重复处理的情况下,应该考虑对输入进行压缩。然而,你无须显示指定使用的编解码方式。
Hadoop
自动检查文件扩展名,如果扩展名能够匹配,就会用恰当的编解码方式对文件进行压缩和解压。否则,Hadoop
就不会使用任何编解码器。 -
Mapper
输出采用压缩:当
Map
任务输出的中间数据量很大时,应考虑在此阶段采用压缩技术。这能显著改善内部数据Shuffle
过程,而Shuffle
过程在Hadoop
处理过程中是资源消耗最多的环节。如果发现数据量大造成网络传输缓慢,应该考虑使用压缩技术。可用于压缩Mapper
输出的快速编解码器包括LZO
或者Snappy
。 -
Reducer
输出采用压缩:在此阶段启用压缩技术能够减少要存储的数据量,因此降低所需的磁盘空间。当
MapReduce
作业形成作业链条时,因为第二个作业的输入也已压缩,所以启用压缩同样有效。 -
注意:
LZO
是供Hadoop
压缩数据用的通用压缩编解码器。其设计目标是达到与硬盘读取速度相当的压缩速度,因此速度是优先考虑的因素,而不是压缩率。与Gzip
编解码器相比,它的压缩速度是Gzip
的5倍,而解压速度是Gzip
的2倍。同一个文件用LZO
压缩后比用Gzip
压缩后大50%
,但比压缩前小25%~50%
。这对改善性能非常有利,Map
阶段完成时间快4倍。
五、压缩参数配置
参数 | 默认值 | 阶段 | 备注 |
---|---|---|---|
io.compression.codecs (在core-site.xml中配置) | org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.BZip2Codec | 输入压缩 | Hadoop使用文件扩展名判断是否支持某种编解码器 |
mapreduce.map.output.compress(在mapred-site.xml中配置) | false | mapper输出 | 这个参数设为true启用压缩 |
mapreduce.map.output.compress.codec(在mapred-site.xml中配置) | org.apache.hadoop.io.compress.DefaultCodec | mapper输出 | 企业多使用LZO或Snappy编解码器在此阶段压缩数据 |
mapreduce.output.fileoutputformat.compress(在mapred-site.xml中配置) | false | reducer输出 | 这个参数设为true启用压缩 |
mapreduce.output.fileoutputformat.compress.codec(在mapred-site.xml中配置) | org.apache.hadoop.io.compress. DefaultCodec | reducer输出 | 使用标准工具或者编解码器,如gzip和bzip2 |
mapreduce.output.fileoutputformat.compress.type(在mapred-site.xml中配置) | RECORD | reducer输出 | SequenceFile输出使用的压缩类型:NONE和BLOCK |
六、压缩案例实操
(一)数据流的压缩和解压缩
-
CompressionCodec
有两个方法可以用于轻松地压缩或解压缩数据:要想对正在被写入一个输出流的数据进行压缩,我们可以使用
createOutputStream(OutputStreamout)
方法创建一个CompressionOutputStream
,将其以压缩格式写入底层的流。相反,要想对从输入流读取而来的数据进行解压缩,则调用
createInputStream(InputStreamin)
函数,从而获得一个CompressionInputStream
,从而从底层的流读取未压缩的数据。 -
案例代码
package com.easysir.zip; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.compress.CompressionCodec; import org.apache.hadoop.io.compress.CompressionCodecFactory; import org.apache.hadoop.io.compress.CompressionInputStream; import org.apache.hadoop.io.compress.CompressionOutputStream; import org.apache.hadoop.util.ReflectionUtils; import java.io.*; public class TestCompress { public static void main(String[] args) throws Exception { //compress("E:\\idea-workspace\\mrWordCount\\input\\web.log","org.apache.hadoop.io.compress.BZip2Codec"); decompress("E:\\idea-workspace\\mrWordCount\\input\\web.log.bz2"); } // 1、压缩 private static void compress(String filename, String method) throws Exception { // (1)获取输入流 FileInputStream fis = new FileInputStream(new File(filename)); Class codecClass = Class.forName(method); CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codecClass, new Configuration()); // (2)获取输出流 FileOutputStream fos = new FileOutputStream(new File(filename + codec.getDefaultExtension())); CompressionOutputStream cos = codec.createOutputStream(fos); // (3)流的对拷 IOUtils.copyBytes(fis, cos, 1024*1024*5, false); // (4)关闭资源 cos.close(); fos.close(); fis.close(); } // 2、解压缩 private static void decompress(String filename) throws FileNotFoundException, IOException { // (0)校验是否能解压缩 CompressionCodecFactory factory = new CompressionCodecFactory(new Configuration()); CompressionCodec codec = factory.getCodec(new Path(filename)); if (codec == null) { System.out.println("cannot find codec for file " + filename); return; } // (1)获取输入流 CompressionInputStream cis = codec.createInputStream(new FileInputStream(new File(filename))); // (2)获取输出流 FileOutputStream fos = new FileOutputStream(new File(filename + ".decoded")); // (3)流的对拷 IOUtils.copyBytes(cis, fos, 1024*1024*5, false); // (4)关闭资源 cis.close(); fos.close(); } }
(二)Map输出端采用压缩
-
即使你的
MapReduce
的输入输出文件都是未压缩的文件,你仍然可以对Map
任务的中间结果输出做压缩,因为它要写在硬盘并且通过网络传输到Reduce
节点,对其压缩可以提高很多性能,这些工作只要在驱动类中设置如下两个属性即可:// 开启map端输出压缩 configuration.setBoolean("mapreduce.map.output.compress", true); // 设置map端输出压缩方式 configuration.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class, CompressionCodec.class);
-
可在
WordCount
案例中修改
(三)Reduce输出端采用压缩
-
Reduce输出端采用压缩,只需在
// 设置reduce端输出压缩开启 FileOutputFormat.setCompressOutput(job, true); // 设置压缩的方式 FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);
-
可在
WordCount
案例中修改