N.1 压缩概述

1)压缩技术能够有效减少底层存储系统(HDFS)读写字节数。压缩提高了网络带宽和磁盘空间的效率。在Hadoop下,尤其是数据规模很大和工作负载密集的情况下,使用数据压缩显得非常重要。在这种情况下,I/O操作和网络数据传输要花大量的时间。还有,Shuffle与Merge过程同样也面临着巨大的I/O压力。鉴于磁盘I/O和网络带宽是Hadoop的宝贵资源,数据压缩对于节省资源、最小化磁盘I/O和网络传输非常有帮助。不过,尽管压缩与解压操作的CPU开销不高,其性能的提升和资源的节省并非没有代价。如果磁盘I/O和网络带宽影响了MapReduce作业性能,在任意MapReduce阶段启用压缩都可以改善端到端处理时间并减少I/O和网络流量。压缩Mapreduce的一种优化策略:通过压缩编码对Mapper或者Reducer的输出进行压缩,以减少磁盘IO,提高MR程序运行速度(但相应增加了cpu运算负担)。注意:压缩特性运用得当能提高性能,但运用不当也可能降低性能。

2)基本原则:

(1)运算密集型的job,少用压缩

(2)IO密集型的job,多用压缩

N.2 MR支持的压缩编码

N.2.1 压缩介绍

压缩格式

hadoop自带?

算法

文件扩展名

是否可切分

换成压缩格式后,原来的程序是否需要修改

DEFAULT

是,直接使用

DEFAULT

.deflate

和文本处理一样,不需要修改

Gzip

是,直接使用

DEFAULT

.gz

和文本处理一样,不需要修改

bzip2

是,直接使用

bzip2

.bz2

和文本处理一样,不需要修改

LZO

否,需要安装

LZO

.lzo

需要建索引,还需要指定输入格式

Snappy

否,需要安装

Snappy

.snappy

和文本处理一样,不需要修改

N.2.1 编码/解码器

1)为了支持多种压缩/解压缩算法,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

N.2.3 压缩性能比较

1)压缩方式一般选择snappy,lzo 这两个在性能上差不多。

压缩算法

原始文件大小

压缩文件大小

压缩速度

解压速度

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

N.3 压缩方式选择

N.3.1 Gzip压缩

1)优点:压缩率比较高,而且压缩/解压速度也比较快;hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样;大部分linux系统都自带gzip命令,使用方便。

2)缺点:不支持split。

3)应用场景:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用gzip压缩格式。例如说一天或者一个小时的日志压缩成一个gzip文件,运行mapreduce程序的时候通过多个gzip文件达到并发。hive程序,streaming程序,和java写的mapreduce程序完全和文本处理一样,压缩之后原来的程序不需要做任何修改。

N.3.2 Bzip2压缩

1)优点:支持split;具有很高的压缩率,比gzip压缩率都高;hadoop本身支持,但不支持native(java和c互操作的API接口);在linux系统下自带bzip2命令,使用方便。

2)缺点:压缩/解压速度慢;不支持native。

3)应用场景:适合对速度要求不高,但需要较高的压缩率的时候,可以作为mapreduce作业的输出格式;或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持split,而且兼容之前的应用程序(即应用程序不需要修改)的情况。

N.3.3 Lzo压缩

1)优点:压缩/解压速度也比较快,合理的压缩率;支持split,是hadoop中最流行的压缩格式;可以在linux系统下安装lzop命令,使用方便。

2)缺点:压缩率比gzip要低一些;hadoop本身不支持,需要安装;在应用中对lzo格式的文件需要做一些特殊处理(为了支持split需要建索引,还需要指定inputformat为lzo格式)。

3)应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越越明显。

N.3.4 Snappy压缩

1)优点:高速压缩速度和合理的压缩率。

2)缺点:不支持split;压缩率比gzip要低;hadoop本身不支持,需要安装;

3)应用场景:当Mapreduce作业的Map输出的数据比较大的时候,作为Map到Reduce的中间数据的压缩格式;或者作为一个Mapreduce作业的输出和另外一个Mapreduce作业的输入。

N.4 压缩配置参数

1)要在Hadoop中启用压缩,可以配置如下参数:

参数<name>..</name>

默认值<value>..</value>

阶段

建议

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

N.5 压缩位置选择

1)压缩可以在MapReduce作用的任意阶段启用代码如下

package study190616_5;
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.*;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class WordCountMain {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
args=new String[]{"C:\\Users\\HMTX\\Desktop\\0616-压缩\\GroupingComparator.txt","C:\\Users\\HMTX\\Desktop\\0616-压缩\\t1"};
//获取配置文件
Configuration conf = new Configuration();
//开启map端的输出压缩
conf.setBoolean("mapreduce.map.output.compress", true);
//设置压缩方式
conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class, CompressionCodec.class);
//创建job任务
Job job = Job.getInstance(conf);
job.setJarByClass(WordCountMain.class);
//指定Map类和map的输出类型 Text, IntWritable
job.setMapperClass(WordCountMap.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定Reducer类和reduce的输出数据类型 Text,IntWritable
job.setReducerClass(WordCountReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//指定数据输入的路径和输出的路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//设置reduce输出压缩
FileOutputFormat.setCompressOutput(job,true);
//设置压缩格式
FileOutputFormat.setOutputCompressorClass(job,BZip2Codec.class);
//提交任务
job.waitForCompletion(true);
}
}

2)单独对文件压缩和解压。

package study190616_4;
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.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Testcompress {
public static void main(String[] args) throws Exception {
//compress("C:\\Users\\HMTX\\Desktop\\0616-压缩\\phone.txt","org.apache.hadoop.io.compress.BZip2Codec");
decompression("C:\\Users\\HMTX\\Desktop\\0616-压缩\\phone.txt.bz2",".txt");
}

//方法参数filename是输入路径,method是编码器
public static void compress(String filename,String method) throws Exception {
//通过反射找到编码器 即压缩。
Class codeClass = Class.forName(method);
//通过工具类获取编码器的对象 同时还需要传入Hadoop的配置
CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codeClass,new Configuration());
//创建文件输入流
FileInputStream fis = new FileInputStream(new File(filename));
//编码器输出流,codec.getDefaultExtension()表示压缩后缀名
FileOutputStream fos = new FileOutputStream(new File(filename+codec.getDefaultExtension()));
CompressionOutputStream cos = codec.createOutputStream(fos);
//对接流,true表示自动关闭流
IOUtils.copyBytes(fis,cos,1024*1024*5,true);
}

//方法参数filename是输出路径,name是后缀名。
public static void decompression(String filename,String name) throws Exception {
//获取实例
CompressionCodecFactory factory = new CompressionCodecFactory(new Configuration());
//获取解码器对象
CompressionCodec codec =factory.getCodec(new Path(filename));
//解码器输入流
CompressionInputStream cis = codec.createInputStream(new FileInputStream(new File(filename)));
//创建文件输出流
FileOutputStream fos = new FileOutputStream(new File(filename+name));
//流对接
IOUtils.copyBytes(cis,fos,1024*1024*5,false);
}
}