Hadoop 从 0 到 1 学习 ——第十二章 Hadoop 数据压缩

1. 压缩概述

压缩计算能够有效减少底层存储系统读写字节数。压缩提高了网络带宽和磁盘空间的效率。在运行 MR 程序时, I/O 操作、网络传输、Shuffle 和 Merge 要花大量的时间,尤其是数据规模很大和工作负载密集的情况下,因此,使用数据压缩显得非常重要

鉴于磁盘 I/O 和网络带宽是 Hadoop 的宝贵资源,数据压缩对于节省资源、最小化磁盘 I/O 和网络传输非常有帮助可以在任意 MapReduce 阶段启用压缩。不过,尽管压缩和解压缩操作的 CPU 开销不高,其型男的提示和资源的节省并发没有代价。

2. 压缩策略和原则

压缩是提高 Hadoop 运行效率的一种优化策略。

通过对 Mapper、Reducer 运行过程的数据进行压缩,以减少磁盘 IO,提高 MR 程序运行速度。

注意:采用压缩技术减少了磁盘 IO,但同时增加了CPU 运算负担。所以、压缩特性运用得当能提升性能,运用不当也可能降低性能。

压缩基本原则:

  1. 运算密集型的 Job,少用压缩。
  2. IO 密集型的 Job、多用压缩。

3. MR 支持的压缩编码

压缩格式hadoop自带?算法文件扩展名是否可切分换成压缩格式后,原来的程序是否需要修改
DEFLATE是,直接使用DEFLATE.deflate和文本处理一样,不需要修改
Gzip是,直接使用DEFLATE.gz和文本处理一样,不需要修改
bzip2是,直接使用bzip2.bz2和文本处理一样,不需要修改
LZO否,需要安装LZO.lzo需要建索引,还需要指定输入格式
Snappy否,需要安装Snappy.snappy和文本处理一样,不需要修改

为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器,如下表所示:

压缩格式对应的编码/解码器
DEFLATEorg.apache.hadoop.io.compress.DefaultCodec
gziporg.apache.hadoop.io.compress.GzipCodec
bzip2org.apache.hadoop.io.compress.BZip2Codec
LZOcom.hadoop.compression.lzo.LzopCodec
Snappyorg.apache.hadoop.io.compress.SnappyCodec

压缩性能的比较

压缩算法原始文件大小压缩文件大小压缩速度解压速度
gzip8.3GB1.8GB17.5MB/s58MB/s
bzip28.3GB1.1GB2.4MB/s9.5MB/s
LZO8.3GB2.9GB49.3MB/s74.6MB/s

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

在64 为 单核 i7 CUP 上,Snappy 的压缩效率是:250MB/s 甚至更高,解压缩效率是 500MB/s甚至更高。

4. 压缩方式选择

4.1 Gzip 压缩

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

缺点:不支持 Split。

应用场景:当每个文件压缩之后再 130M 以内的 (一个块大小内),都可以考虑使用 Gzip 格式压缩。例如说一天或者一个小时的日志压缩成一个 Gzip 文件。

4.2 Bzip2 压缩

优点:支持 Split,具有很高的压缩率,比 Gzip 压缩率都高。Hadoop本身自带,使用方便。

缺点:压缩/解压缩速度慢。

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

4.3 Lzo 压缩

优点:压缩/解压缩速度比较快,合理的压缩率。支持 Split,是 Hadoop 中最流行的压缩格式。可以在 Linux 系统下安装 老婆命令,使用方便。

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

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

4.4 Snappy 压缩

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

缺点:不支持 Split。压缩率比 Gzip 要低;Hadoop 本身不支持,需要安装。

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

5. 压缩位置选择

压缩可以在MapReduce作用的任意阶段启用,如图所示:

在这里插入图片描述

MapReduce 数据压缩

6. 压缩参数配置

要在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中配置)falsemapper输出这个参数设为true启用压缩
mapreduce.map.output.compress.codec(在mapred-site.xml中配置)org.apache.hadoop.io.compress.DefaultCodecmapper输出企业多使用LZO或Snappy编解码器在此阶段压缩数据
mapreduce.output.fileoutputformat.compress(在mapred-site.xml中配置)falsereducer输出这个参数设为true启用压缩
mapreduce.output.fileoutputformat.compress.codec(在mapred-site.xml中配置)org.apache.hadoop.io.compress. DefaultCodecreducer输出使用标准工具或者编解码器,如gzip和bzip2
mapreduce.output.fileoutputformat.compress.type(在mapred-site.xml中配置)RECORDreducer输出SequenceFile输出使用的压缩类型:NONE和BLOCK
配置参数

7. 压缩实操案例

7.1 数据流的压缩和解压缩

CompressionCodec有两个方法可以用于轻松的压缩活解压缩数据。

  • 压缩:需要对数据流的数据进行压缩,我们可以使用 createOutputStream(OutputStreamout)方法创建一个 CompressionOutputStream。将其压缩,写入底层的流。
  • 解压缩:需要对输入流读取来的数据进行解压缩,则需要调用 createInputStream(InputStreamin)函数,从而获得一个 CompressionInputStream,从底层的流读取解压缩的数据。

7.2 测试 Hadoop 压缩方式

DEFLATEorg.apache.hadoop.io.compress.DefaultCodec
gziporg.apache.hadoop.io.compress.GzipCodec
bzip2org.apache.hadoop.io.compress.BZip2Codec
package com.bigdata.hadoop.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.*;
import org.apache.hadoop.util.ReflectionUtils;

public class TestCompress {

    public static void main(String[] args) throws Exception {
        String method = BZip2Codec.class.getSimpleName();
        compress("/data.txt",method);
		decompress("/data.txt.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();
    }
}

7.3 Map 输出端采用压缩

即使你的MapReduce的输入输出文件都是未压缩的文件,你仍然可以对Map任务的中间结果输出做压缩,因为它要写在硬盘并且通过网络传输到Reduce节点,对其压缩可以提高很多性能,这些工作只要设置两个属性即可,我们来看下代码怎么设置。

Hadoop 源码给大家提供的压缩格式有:BZip2CodecDefaultCodec

  1. Mapper保持不变

    package com.bigdata.hadoop.mapreduce.compress;
    
    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;
    
    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 {
    
            // 1 获取一行
            String line = value.toString();
    
            // 2 切割
            String[] words = line.split(" ");
    
            // 3 循环写出
            for(String word:words){
                k.set(word);
                context.write(k, v);
            }
        }
    }
    
  2. Reducer 保持不变

    package com.bigdata.hadoop.mapreduce.compress;
    
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Reducer;
    
    import java.io.IOException;
    
    public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
    
        IntWritable v = new IntWritable();
    
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values,
                              Context context) throws IOException, InterruptedException {
    
            int sum = 0;
    
            // 1 汇总
            for(IntWritable value:values){
                sum += value.get();
            }
    
            v.set(sum);
    
            // 2 输出
            context.write(key, v);
        }
    }
    
  3. Driver 端添加压缩方式

    package com.bigdata.hadoop.mapreduce.compress;
    
    import java.io.IOException;
    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.GzipCodec;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    public class WordCountDriver {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    
            Configuration configuration = new Configuration();
    
            // 开启map端输出压缩
            configuration.setBoolean("mapreduce.map.output.compress", true);
            // 设置map端输出压缩方式
            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 ? 1 : 0);
        }
    }
    

7.4 Reduce 输出端采用压缩

Mapper和Reducer保持不变(详见7.3)

基于WordCount案例处理。

package com.bigdata.hadoop.mapreduce.compress;

import java.io.IOException;
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.GzipCodec;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCountDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration configuration = new Configuration();

        // 开启map端输出压缩
        configuration.setBoolean("mapreduce.map.output.compress", true);
        // 设置map端输出压缩方式
        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]));
        // 设置reduce端输出压缩开启
        FileOutputFormat.setCompressOutput(job, true);

        // 设置压缩的方式
        FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);

        boolean result = job.waitForCompletion(true);

        System.exit(result ? 1 : 0);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值