文章目录
一、概述二、MR支持的压缩编码三、压缩方式选择四、压缩位置选择五、压缩参数配置六、压缩实操案例
一、概述
压缩技术能够有效减少底层存储系统(HDFS)读写字节数。压缩提高了网络带宽和磁盘空间的效率。在运行MR程序时,IO操作、网络数据传输、Shuffle和Merge要花大量的时间,尤其是数据规模很大和工作负载密集的情况下,因此,使用数据压缩显得非常重要。鉴于磁盘I/o和网络带宽是Hadoop的宝贵资源,数据压缩对于节省资源、最小化磁盘I/O和网络传输非常有帮助。可以在任意MapReduce阶段启用压缩。不过,尽管压缩与解压操作的CPU开销不高,其性能的提升和资源的节省并非没有代价。压缩是提高Hadoop运行效率的一种优化策略。通过对Mapper、Reducer运行过程的数据进行压缩,以减少磁盘Io,提高MR程序运行速度。注意:采用压缩技术减少了磁盘Io,但同时增加了CPU运算负担。所以,压缩特性运用得当能提高性能,但运用不当也可能降低性能。压缩基本原则: (1)运算密集型的job,少用压缩 (2)IO密集型的job,多用压缩
二、MR支持的压缩编码
压缩编码 为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 压缩性能的比较
三、压缩方式选择
Gzip压缩 1.1 优点:压缩率比较高,而且压缩/解压速度也比较快;Hadoop本身支持,在应用中处理Gzip格式的文件就和直接处理文本一样;大部分Linux系统都自带Gzip命令,使用方便。 1.2 缺点:不支持Split。 1.3 应用场景:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用Gzip压缩格式。例如说一天或者一个小时的日志压缩成一个Gzip文件。
Bzip2压缩 2.1 优点:支持split;具有很高的压缩率,比Gzip压缩率都高;Hadoop本身自带,使用方便。 2.2 缺点:压缩/解压速度慢。 2.3 应用场景:适合对速度要求不高,但需要较高的压缩率的时候;或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持split,而且兼容之前的应用程序的情况。
Lzo压缩 3.1 优点:压缩/解压速度也比较快,合理的压缩率;支持Split,是Hadoop中最流行的压缩格式;可以在Linux系统下安装lzop命令,使用方便。 3.2 缺点∶压缩率比Gzip要低一些; Hadoop本身不支持,需要安装;在应用中对Lzo格式的文件需要做一些特殊处理(为了支持Split需要建索引,还需要指定InputFormat为Lzo格式)。 3.3 应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而旦单个文件越大,Lzo优点越越明显。
Snappy压缩 4.1 优点:高速压缩速度和合理的压缩率。 4.2 缺点:不支持Split;压缩率比Gzip要低;Hadoop本身不支持,需要安装。 4.3 应用场景:当MapRedce作业的Map输出的数据比较大的时候,作为Map到Reduce的中间数据的压缩格式;或者作为一个MapReduce作业的输出和另外一个MapReduce作业的输入。
四、压缩位置选择
压缩可以在MapReduce作用的任意阶段启用:
五、压缩参数配置
要在Hadoop中启用压缩,可以配置如下参数:
六、压缩实操案例
数据流的压缩和解压缩 CompressionCodec有两个方法可以用于轻松地压缩或解压缩数据。 要想对正在被写入一个输出流的数据进行压缩,我们可以使用createOutputStream(OutputStreamout)方法创建一个CompressionOutputStream,将其以压缩格式写入底层的流。 相反,要想对从输入流读取而来的数据进行解压缩,则调用createInputStream(InputStreamin)函数,从而获得一个CompressionInputStream,从而从底层的流读取未压缩的数据。 测试一下如下压缩方式:
package com
.until
.mapreduce
.compress
;
import java
.io
.File
;
import java
.io
.FileInputStream
;
import java
.io
.FileNotFoundException
;
import java
.io
.FileOutputStream
;
import java
.io
.IOException
;
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
;
public class TestCompress {
public static void main(String
[] args
) throws Exception
{
compress("e:/hello.txt","org.apache.hadoop.io.compress.BZip2Codec");
}
private static void compress(String filename
, String method
) throws Exception
{
FileInputStream fis
= new FileInputStream(new File(filename
));
Class
codecClass = Class
.forName(method
);
CompressionCodec codec
= (CompressionCodec
) ReflectionUtils
.newInstance(codecClass
, new Configuration());
FileOutputStream fos
= new FileOutputStream(new File(filename
+ codec
.getDefaultExtension()));
CompressionOutputStream cos
= codec
.createOutputStream(fos
);
IOUtils
.copyBytes(fis
, cos
, 1024*1024*5, false);
cos
.close();
fos
.close();
fis
.close();
}
private static void decompress(String filename
) throws FileNotFoundException
, IOException
{
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;
}
CompressionInputStream cis
= codec
.createInputStream(new FileInputStream(new File(filename
)));
FileOutputStream fos
= new FileOutputStream(new File(filename
+ ".decoded"));
IOUtils
.copyBytes(cis
, fos
, 1024*1024*5, false);
cis
.close();
fos
.close();
}
}
Map输出端采用压缩 即使你的MapReduce的输入输出文件都是未压缩的文件,你仍然可以对Map任务的中间结果输出做压缩,因为它要写在硬盘并且通过网络传输到Reduce节点,对其压缩可以提高很多性能,这些工作只要设置两个属性即可,我们来看下代码怎么设置。 2.1 提供的Hadoop源码支持的压缩格式有:BZip2Codec 、DefaultCodec 1)WordcountDriver
package com
.until
.mapreduce
;
import org
.apache
.hadoop
.conf
.Configuration
;
import org
.apache
.hadoop
.fs
.Path
;
import org
.apache
.hadoop
.io
.IntWritable
;
import org
.apache
.hadoop
.io
.Text
;
import org
.apache
.hadoop
.io
.compress
.BZip2Codec
;
import org
.apache
.hadoop
.io
.compress
.CompressionCodec
;
import org
.apache
.hadoop
.mapreduce
.Job
;
import org
.apache
.hadoop
.mapreduce
.lib
.input
.FileInputFormat
;
import org
.apache
.hadoop
.mapreduce
.lib
.output
.FileOutputFormat
;
import java
.io
.IOException
;
public class WordcountDriver {
public static void main(String
[] args
) throws IOException
, ClassNotFoundException
, InterruptedException
{
Configuration configuration
= new Configuration();
configuration
.setBoolean("mapreduce.map.output.compress", true);
configuration
.setClass("mapreduce.map.output.compress.codec", BZip2Codec
.class, CompressionCodec
.class);
Job job
= Job
.getInstance(configuration
);
job
.setJarByClass(WordcountDriver
.class);
job
.setMapperClass(WordcountMapper
.class);
job
.setReducerClass(WordcountReducer
.class);
job
.setMapOutputKeyClass(Text
.class);
job
.setMapOutputValueClass(IntWritable
.class);
job
.setOutputKeyClass(Text
.class);
job
.setOutputValueClass(IntWritable
.class);
FileInputFormat
.setInputPaths(job
, new Path(args
[0]));
FileOutputFormat
.setOutputPath(job
, new Path(args
[1]));
boolean result
= job
.waitForCompletion(true);
System
.exit((result
? 0 : 1));
}
}
2)WordcountMapper
package com
.until
.mapreduce
;
import org
.apache
.hadoop
.io
.IntWritable
;
import org
.apache
.hadoop
.io
.LongWritable
;
import org
.apache
.hadoop
.io
.Text
;
import org
.apache
.hadoop
.mapreduce
.Mapper
;
import java
.io
.IOException
;
import java
.nio
.MappedByteBuffer
;
public class WordcountMapper extends Mapper<LongWritable,Text, Text, IntWritable> {
Text k
= new Text();
IntWritable v
= new IntWritable(1);
@Override
protected void map(LongWritable key
, Text value
, Context context
) throws IOException
, InterruptedException
{
String line
= value
.toString();
String
[] words
= line
.split(" ");
for (String word
: words
) {
k
.set(word
);
context
.write(k
,v
);
}
}
}
3)WordcountReducer
package com
.until
.mapreduce
;
import org
.apache
.hadoop
.io
.IntWritable
;
import org
.apache
.hadoop
.io
.Text
;
import org
.apache
.hadoop
.mapreduce
.Reducer
;
import org
.junit
.Test
;
import java
.io
.IOException
;
public class WordcountReducer extends Reducer<Text,IntWritable,Text,IntWritable>{
int sum
;
IntWritable v
= new IntWritable();
@Override
protected void reduce(Text key
, Iterable
<IntWritable> values
, Context context
) throws IOException
, InterruptedException
{
sum
= 0;
for (IntWritable count
: values
) {
sum
+= count
.get();
}
v
.set(sum
);
context
.write(key
,v
);
}
}
Reduce输出端采用压缩 基于WordCount案例处理(修改上面的例子) 修改WordcountDriver类即可,Mapper和Reducer保持不变:
FileOutputFormat
.setCompressOutput(job
, true);
FileOutputFormat
.setOutputCompressorClass(job
, BZip2Codec
.class);