想想一下,当你需要处理500TB的数据的时候,你最先要做的是存储下来。你是选择源文件存储呢?还是处理压缩再存储?很显然,压缩编码处理是必须的。一段刚刚捕获的60分钟原始视屏可能达到2G,经过压缩处理可以减至500MB左右,一张单反照片可能有5MB,经过压缩之后只有400KB,而质量不会发生明显的损失。

hadoop面临的情况也是一样的,大量的数据需要存储在磁盘或者内存中,进行压缩是一种经济的方法。对数据文件进行压缩,可以有效减少存储文件所需的空间,并加快数据在网络上或者到磁盘上的传输速度。在Hadoop中,压缩应用于文件存储、Map阶段到Reduce阶段的数据交换(需要打开相关的选项)等情景。

数据压缩的方式非常多,不同特点的数据有不同的数据压缩方式:如对声音和图像等特殊数据的压缩,就可以采用有损的压缩方法,允许压缩过程中损失一定的信 息,换取比较大的压缩比;而对音乐数据的压缩,由于数据有自己比较特殊的编码方式,因此也可以采用一些针对这些特殊编码的专用数据压缩算法。

hadoop使用的压缩工具主要有:

压缩格式 工具 算法 扩展名 多文件 可分割性
DEFLATE DEFLATE .deflate
GZIP gzip DEFLATE .gzp
ZIP zip DEFLATE .zip 是,在文件范围内
BZIP2 bzip2 BZIP2 .bz2
LZO lzop LZO .lzo


hadoop使用的编码器:


考察压缩工具的性能,主要从时间和空间考虑,一个压缩工具,压缩同样的文件,花费的时间越短,空间越少,压缩率越大,我们就会越喜欢他。另外还要考虑的就是可不可以分割文件,在hadoop中不能够分割文件是个不好的消息。因为hadoop处理数据进行计算的时候,需要将大量的大文件拆分,分割就很重要了。

哪个性能好呢?(下面表格数据引自IBM

压缩算法 原始文件大小 压缩文件大小 压缩速度 解压速度
gzip 8.3GB 1.8GB 17.5MB/s 58MB/s
bzip2 8.3GB 1.1GB 2.4MB/s 9.5MB/s
LZO-bset 8.3GB 2GB 4MB/s 60.6MB/s
LZO 8.3GB 2.9GB 49.3MB/s 74.6MB/s

这还不是全部,hadoop通过压缩流,也就是将文件写进压缩流里面进行数据读写,性能如何呢?



以下引自xuxm2007

CompressionCodec对流进行压缩和解压缩
CompressionCodec有两个方法可以用于轻松地压缩或解压缩数据。要想对正在被写入一个输出流的数据进行压缩,我们可以使用 createOutputStream(OutputStreamout)方法创建一个CompressionOutputStream(未压缩的数据将 被写到此),将其以压缩格式写入底层的流。相反,要想对从输入流读取而来的数据进行解压缩,则调用 createInputStream(InputStreamin)函数,从而获得一个CompressionInputStream,,从而从底层的流
读取未压缩的数据。CompressionOutputStream和CompressionInputStream类似干 java.util.zip.DeflaterOutputStream和java.util.zip.DeflaterInputStream,前两者 还可以提供重置其底层压缩和解压缩功能,当把数据流中的section压缩为单独的块时,这比较重要。比如SequenceFile。

下例中说明了如何使用API来压缩从标谁输入读取的数据及如何将它写到标准输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StreamCompressor 
{
     public static void main(String[] args) throws Exception 
     {
          String codecClassname = args[0];
          Class<?> codecClass = Class.forName(codecClassname); // 通过名称找对应的编码/解码器
          Configuration conf = new Configuration();
CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codecClass, conf);
 // 通过编码/解码器创建对应的输出流

          CompressionOutputStream out = codec.createOutputStream(System.out);
 // 压缩

          IOUtils.copyBytes(System.in, out, 4096, false);
          out.finish();
     }


用CompressionCodecFactory方法来推断CompressionCodecs

在阅读一个压缩文件时,我们通常可以从其扩展名来推断出它的编码/解码器。以.gz结尾的文件可以用GzipCodec来阅读,如此类推。每个压缩格式的扩展名如第一个表格;

CompressionCodecFactory提供了getCodec()方法,从而将文件扩展名映射到相应的CompressionCodec。此方法接受一个Path对象。下面的例子显示了一个应用程序,此程序便使用这个功能来解压缩文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class FileDecompressor {
    public static void main(String[] args) throws Exception {
       String uri = args[0];
       Configuration conf = new Configuration();
       FileSystem fs = FileSystem.get(URI.create(uri), conf);
       Path inputPath = new Path(uri);
       CompressionCodecFactory factory = new CompressionCodecFactory(conf);
       CompressionCodec codec = factory.getCodec(inputPath);
       if (codec == null) {
           System.err.println("No codec found for " + uri);
           System.exit(1);
       }
       String outputUri =
       CompressionCodecFactory.removeSuffix(uri, codec.getDefaultExtension());
       InputStream in null;
       OutputStream out = null;
       try {
           in = codec.createInputStream(fs.open(inputPath));
           out = fs.create(new Path(outputUri));
           IOUtils.copyBytes(in, out, conf);
       } finally {
           IOUtils.closeStream(in);
           IOUtils.closeStream(out);
       }
    }
}

编码/解码器一旦找到,就会被用来去掉文件名后缀生成输出文件名(通过CompressionCodecFactory的静态方法removeSuffix()来实现)。这样,如下调用程序便把一个名为file.gz的文件解压缩为file文件:

% hadoop FileDecompressor file.gz

CompressionCodecFactory 从io.compression.codecs配置属性定义的列表中找到编码/解码器。默认情况下,这个列表列出了Hadoop提供的所有编码/解码器 (见表4-3),如果你有一个希望要注册的编码/解码器(如外部托管的LZO编码/解码器)你可以改变这个列表。每个编码/解码器知道它的默认文件扩展
名,从而使CompressionCodecFactory可以通过搜索这个列表来找到一个给定的扩展名相匹配的编码/解码器(如果有的话)。

 属性名  类型  默认值  描述
io.compression.codecs 逗号分隔的类名 org.apache.hadoop.io.compress.DefaultCodec,

org.apache.hadoop.io.compress.GzipCodec,

org.apache.hadoop.io.compress.Bzip2Codec
用于压缩/解压的CompressionCodec列表

本地库

考虑到性能,最好使用一个本地库(native library)来压缩和解压。例如,在一个测试中,使用本地gzip压缩库减少了解压时间50%,压缩时间大约减少了10%(与内置的Java实现相比 较)。表4-4展示了Java和本地提供的每个压缩格式的实现。井不是所有的格式都有本地实现(例如bzip2压缩),而另一些则仅有本地实现(例如 LZO)。

压缩格式 Java实现 本地实现
DEFLATE
gzip
bzip2
LZO

Hadoop带有预置的32位和64位Linux的本地压缩库,位于库/本地目录。对于其他平台,需要自己编译库,具体请参见Hadoop的维基百科http://wiki.apache.org/hadoop/NativeHadoop。

本地库通过Java系统属性java.library.path来使用。Hadoop的脚本在bin目录中已经设置好这个属性,但如果不使用该脚本,则需要在应用中设置属性。

默认情况下,Hadoop会在它运行的平台上查找本地库,如果发现就自动加载。这意味着不必更改任何配置设置就可以使用本地库。在某些情况下,可能 希望禁用本地库,比如在调试压缩相关问题的时候。为此,将属性hadoop.native.lib设置为false,即可确保内置的Java等同内置实现 被使用(如果它们可用的话)。

CodecPool(压缩解码池)

如果要用本地库在应用中大量执行压缩解压任务,可以考虑使用CodecPool,从而重用压缩程序和解压缩程序,节约创建这些对象的开销。

下例所用的API只创建了一个很简单的压缩程序,因此不必使用这个池。此应用程序使用一个压缩池程序来压缩从标准输入读入然后将其写入标准愉出的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PooledStreamCompressor 
 {
    public static void main(String[] args) throws Exception 
    {
        String codecClassname = args[0];
        Class<?> codecClass = Class.forName(codecClassname);
        Configuration conf = new Configuration();
CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codecClass, conf);
        Compressor compressor = null;
        try {
compressor = CodecPool.getCompressor(codec);//从缓冲池中为指定的CompressionCodec检索到一个Compressor实例
CompressionOutputStream out = codec.createOutputStream(System.out, compressor);
            IOUtils.copyBytes(System.in, out, 4096, false);
            out.finish();
        } finally
        {
            CodecPool.returnCompressor(compressor);
        }
      }

我 们从缓冲池中为指定的CompressionCodec检索到一个Compressor实例,codec的重载方法 createOutputStream()中使用的便是它。通过使用finally块,我们便可确保此压缩程序会被返回缓冲池,即使在复制数据流之间的字 节期间抛出了一个IOException。
压缩和输入分割

在考虑如何压缩那些将由MapReduce处理的数据时,考虑压缩格式是否支持分割是很重要的。考虑存储在HDFS中的未压缩的文件,其大小为1GB. HDFS块的大小为64MB ,所以文件将被存储为16块,将此文件用作输入的MapReduce会创建16个输入分片(split,也称为"分块"),每个分片都被作为一个独立map任务的输入单独进行处理。

现在假设,该文件是一个gzip格式的压缩文件,压缩后的大小为1GB。和前面一样,HDFS将此文件存储为 16块。然而,针对每一块创建一个分块是没有用的因为不可能从gzip数据流中的任意点开始读取,map任务也不可能独立于其分块只读取一个分块中的数据。gZlp格式使用DEFLATE来存储压缩过的数据,DEFLATE
将数据作为一系列压缩过的块进行存储。问题是,每块的开始没有指定用户在数据流中任意点定位到下一个块的起始位置,而是其自身与数据流同步。因此,gzip不支持分割(块)机制。在这种情况下,MapReduce不分割gzip格式的文件,因为它知道输入的是gzip格式(通过文件扩展名得知),而gzip压缩机制不支持分割机制。这样是以牺牲本地化为代价:一个map任务将处理16个HDFS块,大都不是map的本地数据。与此同时,因为map任务少,所以作业分割的粒度不够细,从而导致运行时间变长。

在我们假设的例子中,如果是一个LZO格式的文件,我们会碰到同样的问题,因为基本压缩格式不为reader 提供方法使其与流同步。但是,bzip2格式的压缩文件确实提供了块与块之间的同步标记(一个48位的π近似值) 因此它支持分割机制。对于文件的收集,这些问题会稍有不同。ZIP是存档格式,因此t可以将多个文件合并为一个ZIP文件。每个文单独压缩,所有文档的存储位置存储在ZIP文件的尾部。这个属性表明Z l P 文件支持文件边界处分割,每个分片中包括ZIP压缩文件中的一个或多个文件。

ZIP格式文件的结构如下图:ZIP文件结构请查看wiki


在MapReduce 中使用压缩

如前所述,如果输入的文件是压缩过的.那么在被MapReduce 读取时,它们会被自动解压,根据文件扩展名来决定应该使用哪一个压缩解码器。如果要压缩MapReduce作业的输出.请在作业配置文件中将mapred.output.compress属性设置为true,将mapred.output.compression.codec属性设置为自己打算使用的压缩编码/解码器的类名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    public class MaxTemperatureWithCompression {
 
        public static void main(String[] args) throws Exception {
            if (args.length != 2) {
                System.err.println("Usage: MaxTemperatureWithCompression <input path> "
                        "<output path>");
                System.exit(-1);
            }
            Job job = new Job();
            job.setJarByClass(MaxTemperature.class);
            FileInputFormat.addInputPath(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1]));
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
            FileOutputFormat.setCompressOutput(job, true);
            FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
            job.setMapperClass(MaxTemperatureMapper.class);
            job.setCombinerClass(MaxTemperatureReducer.class);
            job.setReducerClass(MaxTemperatureReducer.class);
            System.exit(job.waitForCompletion(true) ? 0 1);
        }
    }

我们使用压缩过的输入来运行此应用程序(其实不必像它一样使用和输入相同的格式压缩输出),如下所示:
% hadoop MaxTemperatur eWithCompression input/ ncdc/sample.txt.gz
output
量生终输出的每部分都是压缩过的.在本例中,只有一部分:
% gunzip -c output/part-OOOOO.gz
1949 111
1950 22
如果为输出使用了一系列文件, 可以设置mapred.output.compresson.type 属性来控制压缩类型。默认为RECORD,它压缩单独的记录。将它改为BLOCK,可以压缩一组记录,由于有更好的压缩比,所以推荐使用。

map 作业输出结果的压缩
即使MapReduce 应用使用非压缩的数据来读取和写入,我们也可以受益于压缩map阶段的中阔输出。因为map作业的输出会被写入磁盘并通过网络传输到reducer节点,所以如果使用LZO之类的快速压缩,能得到更好的性能,因为传输的数据量大大减少了.表4-5显示了启用map输出压缩和设置压缩格式的配置属性.
下面几行代码用于在map 作业中启用gzi p 格式来压缩输出结果:
1
2
3
4
5
Configuration conf = new Configuration();
conf.setBoolean("mapred.compress.map.output"true);
conf.setClass("mapred.map.output.compression.codec", GzipCodec.class,
CompressionCodec.class);
Job job = new Job(conf);

旧的API要这样配置

1
2
conf.setCompressMapOutput(true);
conf.setMapOutputCompressorClass(GzipCodec.class);

压缩就到此为止了。总之编码和解码在hadoop有着关键的作用。

Charles 于P.P 



版权说明:
本文由Charles Dong原创,本人支持开源以及免费有益的传播,反对商业化谋利。
CSDN博客:http://blog.csdn.net/mrcharles
个人站:http://blog.xingbod.cn
EMAIL:charles@xingbod.cn

Hadoop编码解码【压缩解压缩】机制详解(1)的更多相关文章

  1. Linux/centos/redhat下各种压缩解压缩方式详解

    1.zip命令 zip -r myfile.zip ./* 将当前目录下的所有文件和文件夹全部压缩成myfile.zip文件,-r表示递归压缩子目录下所有文件. 2.unzip unzip -o -d ...

  2. Hadoop框架:DataNode工作机制详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.工作机制 1.基础描述 DataNode上数据块以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是数据块元数据包括长度.校验.时 ...

  3. Linux下的压缩解压缩命令详解

    linux zip命令zip -r myfile.zip ./*将当前目录下的所有文件和文件夹全部压缩成myfile.zip文件,-r表示递归压缩子目录下所有文件. 2.unzipunzip -o - ...

  4. Linux下的压缩解压缩命令详解及实例

    实例:压缩服务器上当前目录的内容为xxx.zip文件 zip -r xxx.zip ./* 解压zip文件到当前目录 unzip filename.zip ====================== ...

  5. Hadoop框架:NameNode工作机制详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.存储机制 1.基础描述 NameNode运行时元数据需要存放在内存中,同时在磁盘中备份元数据的fsImage,当元数据有更新或者添加元数据 ...

  6. tar压缩解压缩命令详解

    tar命令详解 -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 -u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用 ...

  7. linux压缩(解压缩)命令详解

    一.tar命令          tar可以为文件和目录创建档案.利用tar,用户可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件.tar 最初被用来在磁带上创 ...

  8. Linux下的tar压缩解压缩命令详解

    转载自http://www.cnblogs.com/qq78292959/archive/2011/07/06/2099427.html tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压 ...

  9. Linux下的tar压缩解压缩命令详解(转)

    tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...

  10. Linux下tar压缩解压缩命令详解

    tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...

随机推荐

  1. LeetCode OJ:First Missing Positive (第一个丢失的正数)

    在leetCode上做的第一个难度是hard的题,题目如下: Given an unsorted integer array, find the first missing positive inte ...

  2. 剑指offer--28.栈的压入、弹出序列

    时间限制:1秒 空间限制:32768K 热度指数:300132 本题知识点: 栈 算法知识视频讲解 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假 ...

  3. 【tensorflow:Google】二、Tensorflow环境搭建

    2.1 Tensorflow 主要依赖包 2.1.1 Protocol Buffer 结构化数据序列化的过程,另外的工具:XML, JSON, 区别:二进制(不可读):先定义数据格式,还原的时候将需要 ...

  4. 旧书重温:0day2【5】shellcode变形记

    紧接上一篇,结合第一篇 //这篇文章主要成功溢出一个带有缓冲区溢出的小程序,其中我们的shellcode被strcpy截断了所以我们需要变形shellcode,这个实验中也出现了很多意想不到的拦路虎, ...

  5. 演示使用Metasploit入侵Windows

    我使用Kali Linux的IP地址是192.168.0.112:在同一局域网内有一台运行Windows XP(192.168.0.108)的测试电脑. 本文演示怎么使用Metasploit入侵win ...

  6. 为什么选择MpVue进行小程序的开发

    前言 mpvue是一款使用Vue.js开发微信小程序的前端框架.使用此框架,开发者将得到完整的 Vue.js 开发体验,同时为H5和小程序提供了代码复用的能力.如果想将 H5 项目改造为小程序,或开发 ...

  7. W3Schools SQL Quiz

    W3Schools SQL Quiz SQL QUIZ Points: 25 out of 25 1. What does SQL stand for? You answered: Structure ...

  8. 有状态与无状态 cookie session

    服务器所维护的与客户交互活动的信息称为状态信息.不保存任何状态信息的服务器称为无状态服务器(stateless server),反之则称为有状态服务器(stateful server). 面向连接对应 ...

  9. Java Platform SE binary已停止运行 Can't load AMD 64-bit.dll on a IA 32-bit platform错误

    这个我是在junit测试web项目时候遇到的问题,然后今天遇到一位网友也遇到了,报问题的是A卡了,所以今天做个记载,方便遇到问题的人,因为,之前我个人遇到这个问题,在网上找了很久都没有找到. 上面这个 ...

  10. BZOJ4832: [Lydsy2017年4月月赛]抵制克苏恩

    传送门 题目大意: 攻击k次,每次可攻击随从或英雄. 随从数不大于7个,且1滴血的a个,2滴b个,3滴c个. 攻击一次血-1,如果随从没死可以生成3滴血随从一个 题解: 概率/期望dp f[i][j] ...