Hadoop权威指南:压缩
Hadoop权威指南:压缩
文件压缩的两个好处:
- 减少储存文件所需要的磁盘空间
- 加速数据在网络和磁盘上的传输
压缩格式总结:
| 压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 | 
|---|---|---|---|---|
| DEFLATE | 无 | DEFLATE | .deflate | 否 | 
| Gzip | gzip | DEFLATE | .gz | 否 | 
| bzip2 | bzip2 | bzip2 | .bz2 | 是 | 
| LZO | lzop | LZO | .lzo | 否 | 
| LZ4 | 无 | LZ4 | .lz4 | 否 | 
| Snappy | 无 | Snappy | .snapp | 否 | 
上述表中的所有压缩工具都提供9个不同的选项来控制压缩时必须考虑的权衡:
- -1位优化压缩速度
- -9为优化压缩空间
codec
codec 实现了一种压缩-解压算法
Hadoop中一个对CompressionCodec接口的实现代表一个codec
Hadoop的压缩codec
| 压缩格式 | HadoopCompressionCodec | 
|---|---|
| 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 | 
| LZ4 | org.apache.hadoop.io.compress.Lz4Codec | 
| Snappy | org.apache.hadoop.io.compress.SnappyCodec | 
通过CompressionCodec对数据流进行压缩和解压缩
CompressionCodec包含两个函数 可以轻松用于压缩和解压缩数据
- createOutputStream(OutputStream out)方法在底层数据流中对需要以压缩格式写入在此之前尚未压缩的数据新建一个- CompressionOutputStream对象
- 对输入数据流中读取的数据进行解压缩的时候,则调用createInputStream(InputStream in)获取CompressionInputStream,通过该方法从底层数据里读取解压缩后的数据
压缩从标准输入读取的数据,并写到标准输出
代码
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.CompressionOutputStream;
import org.apache.hadoop.util.ReflectionUtils;
import java.io.IOException;
public class StreamCompressor {
    public static void main(String  args[]) throws ClassNotFoundException, IOException {
        // 符合ReflectionUtils的实现
        String codecClassname = args[0];
        Class<?> codecClass = Class.forName(codecClassname);
        Configuration conf = new Configuration();
        // 新建codec实例
        CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codecClass, conf);
        // 获得在System.out上支持压缩的一个包裹方法
        CompressionOutputStream out = codec.createOutputStream(System.out);
        // 对IOUtils对象调用copyBytes()方法将输入数据复制到输出
        // 输出由CompressionOutputStream对象压缩
        IOUtils.copyBytes(System.in, out, 4096, false);
        // 要求压缩方法完成到压缩数据流的写操作
        out.finish();
    }
}
// echo "Test" | hadoop StreamCompressor org.apache.hadoop.io.compress.GzipCodec | gunzip
编译
javac StreamCompressor.java
测试
压缩从标准输入读取的数据,并写到标准输出,通过管道传递给gunzip, 显示压缩内容
echo "Test" | hadoop StreamCompressor org.apache.hadoop.io.compress.GzipCodec | gunzip
通过CompressionCodecFactory推断CompressionCodec
根据文件扩展名选取codec解压缩文件
代码
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
public class FileDecompressor {
    public static void main(String args[]) throws IOException {
        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的getCodec()方法, 得到相应的CompressionCodec或者null即CompressionCodecFactory可通过搜索注册的codec找到匹配指定文件扩展名的codec
- 如果压缩的是单个文本文件, 可以直接使用cat名查看解压缩后生成的文件
编译
javac FileDecompressor.java
运行
hadoop FileDecompressor /usr/hadoop/file.gz
- file.gz要在hdfs中存在
- 会被解压在hdfs中名为file的文件
压缩代码库的实现
- 使用原生类库来实现解压缩, 会节约解压缩的时间.
- 可以通过Java系统的java.library.path属性指定原生代码库
| 压缩格式 | 是否有Java实现 | 是否有原生(native)实现 | 
|---|---|---|
| DEFLATE | 是 | 是 | 
| gzip | 是 | 是 | 
| bzip2 | 是 | 否 | 
| LZO | 否 | 是 | 
| LZ4 | 否 | 是 | 
| Snappy | 否 | 是 | 
CodecPool
- CodecPool对象支持反复使用压缩和解压缩操作, 以减少创建对象的开销
使用压缩池对读取自标准输入的数据进行压缩,然后将其写到标准输出
代码
/*
* Hadoop权威指南:第四章
* */
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.compress.CodecPool;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.CompressionOutputStream;
import org.apache.hadoop.io.compress.Compressor;
import org.apache.hadoop.oncrpc.security.SysSecurityHandler;
import org.apache.hadoop.util.ReflectionUtils;
import java.io.IOException;
public class PooledStreamCompressor {
    public static void main(String args[]) throws ClassNotFoundException, IOException {
        String codecClassName = args[0];
        // 找到对应CompressionCodec类
        Class<?> codeClass = Class.forName(codecClassName);
        Configuration conf = new Configuration();
        // 通过类名创建实例对象
        CompressionCodec codec = (CompressionCodec) ReflectionUtils.newInstance(codeClass, conf);
        Compressor compressor = null;
        try {
            // 得到compressor
            compressor = CodecPool.getCompressor(codec);
            CompressionOutputStream out = codec.createOutputStream(System.out, compressor);
            IOUtils.copyBytes(System.in, out, 4096, false);
            out.flush();
        } finally {
            // 将compressor返回CodecPool中
            CodecPool.returnCompressor(compressor);
        }
    }
}
- 在codec的重载方法createOutputStream中,对于制定的CompressionCodec,我们从池中获取一个Compressor实例
压缩和输入分片
- 了解这些压缩格式是否支持切分非常重要,例如, 文件压缩为某种个格式后,为1G, 它将被储存成16块(每块是64M的话), 如果该压缩格式不支持切分, 那么就每个块就不能单独作为输入文件,无法实现从压缩数据流任意位置读取数据
应该使用哪种压缩格式?
大致按照效率从高到低排列:
- 适应容器文件格式,例如顺序文件,REFile或者Avro数据问津, 这些格式同时支持压缩和切分.通常最好与一个快速压缩工具联合使用,例如:LZO,LZ4或者Snappy
- 使用支持切分的压缩格式, 例如bzip2, 或者使用通过索引实现切分的压缩格式, 例如:LZO
- 在应用中将文件切分成块,并使用任意一种压缩格式为每个数据块建立压缩文件(不论它是否支持切分).这时, 需要合理选择数据块大小, 以确保压缩后数据块的大小近似于HDFS块的大小
- 储存未经压缩的文件
对于大文件来说,不要使用不支持切分整个文件的压缩格式,因为会失去数据的本地特性,进而造成MapReduce应用效率低下
在MapReduce中使用压缩
解压缩输入
- 如果输入文件是压缩的,那么在根据文件扩展名推断出相应的codec后,MapReduce会在读取文件是自动解压缩文件
解压缩输出
- 要想压缩MapReduce作业的输出,应在作业配置过程中将mapred.output.compress设为true和mapred.output.compression.codec属性设置为打算使用的压缩codec的类名
- 要想压缩MapReduce作业的输出,还可以在FileOutputFormat中使用更便捷的方法设置这些属性
对查找最高温作业所产生输出进行压缩
代码
/*
* Hadoop权威指南:第四章
* */
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
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;
public class MaxTemperatureWithCompression {
    public static void main(String args[]) throws IOException, ClassNotFoundException, InterruptedException {
        if (args.length != 2) {
            System.err.println("Usage: MaxTemperatureWithCompression <input path> <output path>");
            System.exit(-1);
        }
        Job job = new Job();
        job.setJarByClass(MaxTempreture.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 MaxTemperatureWithCompression input/sample.txt.gz output
最终输出的每个部分都是经过压缩的
查看输出
gunzip -c output/part-c-00000.gz
为输出生成顺序文件(sequence file)
- 可以设置mapred.output.compression.type属性来控制限制使用压缩格式,默认值为RECORD, 即对每一条记录进行压缩, 如果将其改为BLOCK,将针对一组记录进行压缩
- 在SequenceFileOutputFormat类中还有一个静态方法putCompressionType()可用来便捷地设置该属性
MapReduce压缩格式
下表归纳概述了用于设置MapReduce作业输出的压缩格式的配置属性
| 属性名称 | 类型 | 默认值 | 描述 | 
|---|---|---|---|
| mapred.output.compress | boolean | false | 压缩输出 | 
| mapred.output.compression.codec | 类名称 | org.apache.hadoop.io.compress.DefaultCodec | map输出所用的压缩codec | 
| mapred.output.compression.type | String | RECORD | SqeuenceFile的输出可以使用的压缩类型:NONE,RECORD,BLOCK | 
对map任务输出进行压缩
map任务的输出需要写到磁盘并通过网络传输到reducer节点,所以如果使用LZO,LZ4或者Snappy这样的快速压缩方式,是可以获取性能提升的
map任务输出的压缩属性
| 属性名称 | 类型 | 默认值 | 描述 | 
|---|---|---|---|
| mapred.compress.map.output | boolean | false | 对map任务输出进行压缩 | 
| mapred.map.output.compression.codec | Class | org.apache.hadoop.io.compress.DefaultCodec | map输出所有的压缩codec | 
示例代码
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);
Hadoop权威指南:压缩的更多相关文章
- 基于python的《Hadoop权威指南》一书中气象数据下载和map reduce化数据处理及其可视化
		文档内容: 1:下载<hadoop权威指南>中的气象数据 2:对下载的气象数据归档整理并读取数据 3:对气象数据进行map reduce进行处理 关键词:<Hadoop权威指南> ... 
- 《Hadoop权威指南》读书笔记1
		<Hadoop权威指南>读书笔记 Day1 第一章 1.MapReduce适合一次写入.多次读取数据的应用,关系型数据库则更适合持续更新的数据集. 2.MapReduce是一种线性的可伸缩 ... 
- Hadoop权威指南(中文版,第2版)【分享】
		下载地址 Hadoop权威指南(中文版,第2版) http://download.csdn.net/download/u011000529/5726789 (友情提示:请点击右下的 “联通下载” 或者 ... 
- Hadoop权威指南学习笔记二
		MapReduce简单介绍 声明:本文是本人基于Hadoop权威指南学习的一些个人理解和笔记,仅供学习參考,有什么不到之处还望指出,一起学习一起进步. 转载请注明:http://blog.csdn.n ... 
- Hadoop权威指南:MapReduce应用开发
		Hadoop权威指南:MapReduce应用开发 [TOC] 一般流程 编写map函数和reduce函数 编写驱动程序运行作业 用于配置的API Hadoop中的组件是通过Hadoop自己的配置API ... 
- Hadoop权威指南:通过FileSystem API读取数据
		Hadoop权威指南:通过FileSystem API读取数据 [TOC] 在Hadoop中,FileSystem是一个通用的文件系统API 获取FileSystem实例的几个静态方法 public ... 
- Hadoop权威指南:从Hadoop URL读取数据
		[TOC] Hadoop权威指南:从Hadoop URL读取数据 使用java.net.URL对象从Hadoop文件系统读取文件 实现类似linux中cat命令的程序 文件名 HDFSCat.java ... 
- Hadoop权威指南:数据完整性
		Hadoop权威指南:数据完整性 [TOC] 常用的错误检测码是CRC-32(循环冗余校验) HDFS的数据完整性 HDFS会对写入的所有数据计算校验和,并在读取数据时验证校验和 datanode负责 ... 
- Hadoop权威指南:HDFS-Hadoop存档
		Hadoop权威指南:HDFS-Hadoop存档 [TOC] 每个文件按块方式存储, 每个块的元数据存储在namenode的内存中 Hadoop存档文件或HAR文件是一个更高效的文件存档工具,它将文件 ... 
随机推荐
- JOptionPane的使用
			最近在做swing程序中遇到使用消息提示框的,JOptionPane类其中封装了很多的方法. 很方便的,于是就简单的整理了一下. 1.1 showMessageDialog 显示一个带有OK 按钮的模 ... 
- Delphi中unicode转汉字函数(转)
			源:Delphi中unicode转汉字函数 近期用到这个函数,无奈没有找到 delphi 自带的,网上找了下 有类似的,没有现成的,我需要的是 支持 “\u4f00 ” 这种格式的,即前面带标准的 “ ... 
- linux 驱动入门2
			不吃苦中苦,难为人上人.努力,给老婆孩子提供个良好的生活居住环境. http://www.cnblogs.com/nan-jing/articles/5775038.html 这里提到.有这么多牛人. ... 
- STM32驱动DHT11温湿度传感器
			DHT11 是一款湿温度一体化的数字传感器.该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接.通过单片机等微处理器简单的电路连接就能够 实时的采集本地湿度和温 ... 
- 全方位分析Objcetive-C Runtime
			本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 简介 与Runtime交互 ... 
- jQuery源码学习(2):选择器初窥
			选择器初窥 代码架构: jQuery选择器可以依照传入数据的类型分为五大类: 传入字符串:$("div"), $("#id"), $(".div1&q ... 
- 多线程的并发问题,lock用法
			开启多个线程,每个线程中多次操作公共变量 using System; using System.Collections.Generic; using System.Linq; using System ... 
- C++类的存储(部分可用与c的结构体)
			c++中最重要的就是类,那么给你一个类的对象,你知道它在内存中如何存储的吗?它占内存中多少个字节? 首先确定类的构成: 1,数据成员:可以是内置类型,类类型. 2,函数成员:虚函数,非虚函数 1)数据 ... 
- Java链表的一些操作:
			[还有一些其他的算法提]http://blog.csdn.net/WalkingInTheWind/article/category/906980 [转]http://blog.csdn.net/lu ... 
- 安卓 canvas
			[转]http://blog.sina.com.cn/s/blog_61ef49250100qw9x.html(easy) [转]http://blog.csdn.net/rhljiayou/arti ... 
