在MR中,压缩是个可选项,是为了减少IO流次数
一、概述
压缩技术能够有效减少底层存储系统(HDFS)读写字节数。压缩提高了网络带宽和磁盘空间的效率。在运行MR程序时,I/O操作、网络数据传输、 Shuffle和Merge要花大量的时间,尤其是数据规模很大和工作负载密集的情况下,因此,使用数据压缩显得非常重要。
鉴于磁盘I/O和网络带宽是Hadoop的宝贵资源,数据压缩对于节省资源、最小化磁盘I/O和网络传输非常有帮助。可以在任意MapReduce阶段启用压缩。不过,尽管压缩与解压操作的CPU开销不高,其性能的提升和资源的节省并非没有代价。
二、压缩的策略和原则
压缩是提高Hadoop运行效率的一种优化策略。
通过对Mapper、Reducer运行过程的数据进行压缩,以减少磁盘IO,提高MR程序运行速度。
注意:采用压缩技术减少了磁盘IO,但同时增加了CPU运算负担。所以,压缩特性运用得当能提高性能,但运用不当也可能降低性能。
压缩基本原则:
(1)运算密集型的job,少用压缩
(2)IO密集型的job,多用压缩
三、MR支持的压缩编码
压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 |
DEFAULT | 无 | DEFAULT | .deflate | 否 |
Gzip | gzip | DEFAULT | .gz | 否 |
bzip2 | bzip2 | bzip2 | .bz2 | 是 |
LZO | lzop | LZO | .lzo | 是 |
LZ4 | 无 | LZ4 | .lz4 | 否 |
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.LzoCodec |
LZ4 | org.apache.hadoop.io.compress.Lz4Codec |
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、LZO在我们MR中用的比较多
1、Gzip压缩
优点:压缩率比较高,而且压缩/解压速度也比较快;Hadoop本身支持,在应用中处理Gzip格式的文件就和直接处理文本一样;大部分Linux系统都自带Gzip命令,使用方便。
缺点:不支持Split。
应用场景:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用Gzip压缩格式。例如说一天或者一个小时的日志压缩成一个Gzip文件。
2、Bzip2压缩
优点:支持Split;具有很高的压缩率,比Gzip压缩率都高;Hadoop本身自带,使用方便。
缺点:压缩/解压速度慢。
应用场景︰适合对速度要求不高,但需要较高的压缩率的时候;或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况,或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持Split,而且兼容之前的应用程序的情况。
3、Lzo压缩
优点∶压缩/解压速度也比较快,合理的压缩率;支持Split,是Hadoop中最流行的压缩格式;可以在Linux系统下安装lzop命令,使用方便。
缺点︰压缩率比Gzip要低一些;Hadoop本身不支持,需要安装,在应用中对Lzo格式的文件需要做一些特殊处理(为了支持Split需要建索引,还需要指定InputFormat为Lzo格式)。
应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,Lzo优点越越明显。
4、Snappy压缩
优点:高速压缩速度和合理的压缩率。
缺点:不支持Split;压缩率比Gzip要低; Hadoop本身不支持,需要安装。
应用场景:当MapReduce作业的Map输出的数据比较大的时候,作为Map到Reduce的中间数据的压缩格式;或者作为一个MapReduce作业的输出和另外一个MapReduce作业的输入。
四、采用压缩的位置
1)输入压缩:
在有大量数据并计划重复处理的情况下,应该考虑对输入进行压缩。然而,你无须显示指定使用的编解码方式。Hadoop自动检查文件扩展名,如果扩展名能够匹配,就会用恰当的编解码方式对文件进行压缩和解压。否则,Hadoop就不会使用任何编解码器。
2)压缩mapper输出:
当map任务输出的中间数据量很大时,应考虑在此阶段采用压缩技术。这能显著改善内部数据Shuffle过程,而Shuffle过程在Hadoop处理过程中是资源消耗最多的环节。如果发现数据量大造成网络传输缓慢,应该考虑使用压缩技术。可用于压缩mapper输出的快速编解码器包括LZO、LZ4或者Snappy。
注:LZO是供Hadoop压缩数据用的通用压缩编解码器。其设计目标是达到与硬盘读取速度相当的压缩速度,因此速度是优先考虑的因素,而不是压缩率。与gzip编解码器相比,它的压缩速度是gzip的5倍,而解压速度是gzip的2倍。同一个文件用LZO压缩后比用gzip压缩后大50%,但比压缩前小25%~50%。这对改善性能非常有利,map阶段完成时间快4倍。
3)压缩reducer输出:
在此阶段启用压缩技术能够减少要存储的数据量,因此降低所需的磁盘空间。当mapreduce作业形成作业链条时,因为第二个作业的输入也已压缩,所以启用压缩同样有效。
总结一下,Mapreduce的压缩支持的位置一共有如下几个位置:
map的输入文件可以支持压缩
map输出数据的时候也支持压缩
reducer输出数据的时候也支持压缩
注意:
MR程序只需要管压缩,不需要管解压缩,因为MR程序在读取压缩文件的时候会根据压缩文件类型自动解压缩
压缩说白了就是把一个文件创建一个输入IO流 然后创建一个压缩工具的输出的IO流 将输入流的数据传递给输出IO流就可以实现压缩
五、案例
1、测试hadoop的压缩机制---将一个文件压缩成为Hadoop支持的一个压缩包
数据流的压缩和解压缩
CompressionCodec有两个方法可以用于轻松地压缩或解压缩数据。要想对正在被写入一个输出流的数据进行压缩,我们可以使用createOutputStream(OutputStreamout)方法创建一个CompressionOutputStream,将其以压缩格式写入底层的流。
相反,要想对从输入流读取而来的数据进行解压缩,则调用createInputStream(InputStreamin)函数,从而获得一个CompressionInputStream,从而从底层的流读取未压缩的数据。
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
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.CompressionOutputStream;
import org.apache.hadoop.util.ReflectionUtils;
import java.net.URI;
public class Demo {
public static void main(String[] args) throws Exception{
/**
* 测试hadoop的压缩机制---将一个文件压缩成为Hadoop支持的一个压缩包
*/
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://192.168.10.3:9000"),conf,"root");
//指定压缩文件
String fileName = "/web.log";
//指定我们使用的压缩工具(使用反射机制)
// String className="org.apache.hadoop.io.compress.DefaultCodec";
String className="org.apache.hadoop.io.compress.GzipCodec";
// String className="org.apache.hadoop.io.compress.BZip2Codec";
//这个代表根据 压缩工具的全限定类名创建一个压缩工具的编解码器
CompressionCodec cc = (CompressionCodec) ReflectionUtils.newInstance(Class.forName(className),conf);
//bizp的后缀名是.bz2 但是压缩的时候我们不能这样拼接
String outPath=fileName+cc.getDefaultExtension();//获取默认后缀名
//压缩就是将文件重新以编码器的IO流输出
FSDataInputStream inputStream = fs.open(new Path(fileName));
//自己创建的一个普通输出流
FSDataOutputStream fsDataOutputStream = fs.create(new Path(outPath));
//根据普通的输出流创建的一个 压缩输出流
CompressionOutputStream outputStream = cc.createOutputStream(fsDataOutputStream);
IOUtils.copyBytes(inputStream,outputStream,1024*1024,false);
//其中第三个参数指copy的字节大小,第四个参数代表是否关闭流
outputStream.close();
inputStream.close();
}
}
2、压缩操作执行成功后,我们来看一下解压缩
解压缩--- 需要创建一个文件的解码输入流
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
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 java.net.URI;
public class Demo01 {
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://192.168.10.3:9000"),conf,"root");
/**
* 解压缩一个压缩包
*/
//1、校验压缩包支不支持解压缩(先创建一个编码器工厂)
CompressionCodecFactory factory = new CompressionCodecFactory(conf);
//当前这个文件用的什么编码器 如果没有对应编码器 那么返回null
Path path = new Path("/web.log");//此时会输出该文件不支持解压缩,操作时我们加上后缀名
CompressionCodec codec = factory.getCodec(path);
if (codec == null){
System.out.println("该文件不支持解压缩");
}else{
//解压缩先创建一个输入流读取文件
CompressionInputStream inputStream = codec.createInputStream(fs.open(path));
//创建输出流放解压缩的位置
FSDataOutputStream fsDataOutputStream = fs.create(new Path("/web-gz.log"));
IOUtils.copyBytes(inputStream,fsDataOutputStream,1024*1024,false);
fsDataOutputStream.close();
inputStream.close();
}
}
}
真正在MR中 底层就是使用这种机制进行压缩和解压缩的。我们使用的时候,不需要写怎么压缩怎么解压缩,我们只需要指定是否开启压缩,采用什么压缩工具 那么MR底层会帮助我们自动完成
喜