Hadoop数据压缩方式对比

1 概述

压缩技术能够有效减少底层存储系统(HDFS)读写字节数。压缩提高了网络带宽和磁盘空间的效率。

注意:压缩特性运用得当能提高性能,但运用不当也可能降低性能。

基本原则:

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

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

主要从压缩率,压缩解压速度,是否可切分三个方面去对比

2 MR支持的压缩编码

压缩格式

hadoop自带?

算法

文件扩展名

是否可切分

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

Deflate

是,直接使用

Defalate

.deflate

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

Gzip

是,直接使用

Defalate

.gz

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

bzip2

是,直接使用

bzip2

.bz2

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

LZO

否,需要安装

LZO

.lzo

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

Snappy

否,需要安装

Snappy

.snappy

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

压缩格式

对应的编码/解码器

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

http://google.github.io/snappy/

On a single core of a Core i7 processor in 64-bit mode, Snappy compresses at about 250 MB/sec or more and decompresses at about 500 MB/sec or more.

3 压缩方式选择

3.1 Gzip压缩

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

缺点:不支持split。

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

3.2 Bzip2压缩

优点:支持split;具有很高的压缩率,比gzip压缩率都高;hadoop本身支持,但不支持native;在linux系统下自带bzip2命令,使用方便。

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

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

3.3 Lzo压缩

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

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

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

3.4 Snappy压缩

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

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

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

目前企业中常用的是Lzo和Snappy

4 压缩参数配置

压缩可以在MapReduce作用的任意阶段启用,如map输入端,mapper输出,reducer输出

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

参数

默认值

阶段

建议

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

 5 案例

5.1 数据流的压缩与解压缩(Map数据输入端)

5.1.1 压缩

package com.caimh.compressioncodec;

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.*;

/**
 * Created by caimh on 2019/9/19.
 */
public class TestCompressionCodec {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //待解压文件
        String filePath = "E:\\hdfsClient\\compress\\领域驱动设计模式、原理与实践.pdf";//待压缩文件路径
//        String filePath = "E:\\hdfsClient\\compress\\领域驱动设计模式、原理与实践.pdf.bz2";//待解压文件路径
        //编解码器名称
        String className = "org.apache.hadoop.io.compress.BZip2Codec";//压缩文件需要
        //压缩文件
        compressFile(filePath, className);
        //解压文件
//        decompressFile(filePath);
    }

    private static void decompressFile(String filePath) throws IOException {
        //1.获取输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //2.获取编码器(解压过程,通过编码器工厂,通过文件扩展名获取对应的编码器)
        Configuration conf = new Configuration();
        CompressionCodecFactory codecFactory = new CompressionCodecFactory(conf);
        CompressionCodec codec = codecFactory.getCodec(new Path(filePath));//根据文件扩展名获取对应的编码器
        if (codec == null) {
            return;
        }
        //3.获取压缩输入流
        CompressionInputStream cis = codec.createInputStream(bis);
        //4.输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath.substring(0, filePath.lastIndexOf("."))));
        //5.拷贝流
        IOUtils.copyBytes(cis, bos, conf);
        //6.释放资源
        IOUtils.closeStream(bis);
        IOUtils.closeStream(cis);
        IOUtils.closeStream(bos);
    }

    private static void compressFile(String filePath, String className) throws ClassNotFoundException, IOException {
        //1输入流读取文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //2获取编解码器
        Class clz = Class.forName(className);
        Configuration conf = new Configuration();
        CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(clz, conf);
        //3压缩文件
        //3.1普通输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath + codec.getDefaultExtension()));
        //3.2压缩输出流(包装普通输出流)
        CompressionOutputStream cos = codec.createOutputStream(bos);
        //3.3 拷贝流
        IOUtils.copyBytes(bis, cos, conf);
        //4 释放资源
        IOUtils.closeStream(bis);
        IOUtils.closeStream(bos);
        IOUtils.closeStream(cos);
    }

}

 案例结果

 5.1.2 解压

package com.caimh.compressioncodec;

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.*;

/**
 * Created by caimh on 2019/9/19.
 */
public class TestCompressionCodec {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //待解压文件
//        String filePath = "E:\\hdfsClient\\compress\\领域驱动设计模式、原理与实践.pdf";//待压缩文件路径
        String filePath = "E:\\hdfsClient\\compress\\领域驱动设计模式、原理与实践.pdf.bz2";//待解压文件路径
        //编解码器名称
//        String className = "org.apache.hadoop.io.compress.BZip2Codec";//压缩文件需要
        //压缩文件
//        compressFile(filePath, className);
        //解压文件
        decompressFile(filePath);
    }

    private static void decompressFile(String filePath) throws IOException {
        //1.获取输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //2.获取编码器(解压过程,通过编码器工厂,通过文件扩展名获取对应的编码器)
        Configuration conf = new Configuration();
        CompressionCodecFactory codecFactory = new CompressionCodecFactory(conf);
        CompressionCodec codec = codecFactory.getCodec(new Path(filePath));//根据文件扩展名获取对应的编码器
        if (codec == null) {
            return;
        }
        //3.获取压缩输入流
        CompressionInputStream cis = codec.createInputStream(bis);
        //4.输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath.substring(0, filePath.lastIndexOf("."))));
        //5.拷贝流
        IOUtils.copyBytes(cis, bos, conf);
        //6.释放资源
        IOUtils.closeStream(bis);
        IOUtils.closeStream(cis);
        IOUtils.closeStream(bos);
    }

    private static void compressFile(String filePath, String className) throws ClassNotFoundException, IOException {
        //1输入流读取文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //2获取编解码器
        Class clz = Class.forName(className);
        Configuration conf = new Configuration();
        CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(clz, conf);
        //3压缩文件
        //3.1普通输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath + codec.getDefaultExtension()));
        //3.2压缩输出流(包装普通输出流)
        CompressionOutputStream cos = codec.createOutputStream(bos);
        //3.3 拷贝流
        IOUtils.copyBytes(bis, cos, conf);
        //4 释放资源
        IOUtils.closeStream(bis);
        IOUtils.closeStream(bos);
        IOUtils.closeStream(cos);
    }

}

5.2 Map输出端采用压缩

package com.caimh.mr.wordcounttest;


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;

/**
 * 这个类是MR程序运行的主类,本类中组装了一些程序运行时候所需的信息
 * 比如:使用的是哪个Mapper类,哪个Reducer类,输入数据在哪儿,输出数据在什么地方
 * <p>
 * Created by caimh on 2019/9/10.
 */
public class WordCountDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1通过Job对象来封装本次的mr的相关信息
        final Configuration conf = new Configuration();
        //指定mapreduce本地运行,也可以不指定,本地mapreduce-default.xml文件默认配置就是local
        //conf.set("mapreduce.framwork.name","local");
        //开启map端输出压缩
        conf.setBoolean("mapreduce.map.output.compress",true);
        //设置map端输出压缩方式
        conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class, CompressionCodec.class);
        final Job job = Job.getInstance(conf);

        //2指定本次mr job jar包运行主类
        job.setJarByClass(WordCountDriver.class);

        //3指定本次mr 所用的mapper  reducer类分别是什么
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        //4指定本次mr mapper阶段的输出  k  v 类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //5指定本次mr 最终输出的 k  v类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //6指定本次mr 数据输入路径及最终结果输出路径
        FileInputFormat.setInputPaths(job, new Path("E:/hdfsClient/cmh.txt"));
        FileOutputFormat.setOutputPath(job, new Path("E:/hdfsClient/wordcount"));

        //7提交job
        final boolean bool = job.waitForCompletion(true);

        //8退出程序
        System.exit(bool ? 0 : 1);
    }
}

5.3 Reduce输出端采用压缩

package com.caimh.mr.wordcounttest;


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.io.compress.DefaultCodec;
import org.apache.hadoop.io.compress.GzipCodec;
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;

/**
 * 这个类是MR程序运行的主类,本类中组装了一些程序运行时候所需的信息
 * 比如:使用的是哪个Mapper类,哪个Reducer类,输入数据在哪儿,输出数据在什么地方
 * <p>
 * Created by caimh on 2019/9/10.
 */
public class WordCountDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1通过Job对象来封装本次的mr的相关信息
        final Configuration conf = new Configuration();
        //指定mapreduce本地运行,也可以不指定,本地mapreduce-default.xml文件默认配置就是local
        //conf.set("mapreduce.framwork.name","local");
        //开启map端输出压缩
        conf.setBoolean("mapreduce.map.output.compress",true);
        //设置map端输出压缩方式
        conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class, CompressionCodec.class);
        final Job job = Job.getInstance(conf);

        //2指定本次mr job jar包运行主类
        job.setJarByClass(WordCountDriver.class);

        //3指定本次mr 所用的mapper  reducer类分别是什么
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        //4指定本次mr mapper阶段的输出  k  v 类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //5指定本次mr 最终输出的 k  v类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //6指定本次mr 数据输入路径及最终结果输出路径
        FileInputFormat.setInputPaths(job, new Path("E:/hdfsClient/cmh.txt"));
        FileOutputFormat.setOutputPath(job, new Path("E:/hdfsClient/wordcount"));

        //7设置压缩方式
        //设置reduce端输出压缩开启
        FileOutputFormat.setCompressOutput(job,true);
        //设置压缩方式
        FileOutputFormat.setOutputCompressorClass(job,BZip2Codec.class);
        FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
        FileOutputFormat.setOutputCompressorClass(job,DefaultCodec.class);

        //8提交job
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

压缩结果

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值