你是选择源文件存储呢?还是处理压缩再存储?非常显然,压缩编码处理是必须的。一段刚刚捕获的60分钟原始视屏可能达到2G,经过压缩处理能够减至500MB左右。一张单反照片可能有5MB。经过压缩之后仅仅有400KB,而质量不会发生明显的损失。
在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处理数据进行计算的时候,须要将大量的大文件拆分,切割就非常重要了。
压缩算法 | 原始文件大小 | 压缩文件大小 | 压缩速度 | 解压速度 |
---|---|---|---|---|
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通过压缩流,也就是将文件写进压缩流里面进行数据读写,性能怎样呢?
要想对正在被写入一个输出流的数据进行压缩。我们能够使用 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(); } } |
在阅读一个压缩文件时,我们通常能够从其扩展名来判断出它的编码/解码器。以.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); } } } |
% hadoop FileDecompressor
file
.gz
属性名 | 类型 | 默认值 | 描写叙述 |
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仅仅创建了一个非常easy的压缩程序。因此不必使用这个池。此应用程序使用一个压缩池程序来压缩从标准输入读入然后将其写入标准愉出的数据:
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<? 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); } } } |
因此,gzip
不支持切割(块)机制。在这样的情况下,MapReduce不切割gzip格式的文件,由于它知道输入的是gzip格式(通过文件扩展名得知),而gzip压缩机制不支持切割机制。这样是以牺牲本地化为代价:一个map任务将处理16个HDFS块,大都不是map的本地数据。与此同一时候,由于map任务少,所以作业切割的粒度不够细,从而导致执行时间变长。可是,bzip2格式的压缩文件
确实提供了块与块之间的同步标记(一个48位的π近似值) 因此它支持切割机制。对于文件的收集。这些问题会稍有不同。ZIP是存档格式,因此t能够将多个文件合并为一个ZIP文件。每一个文单独压缩。全部文档的存储位置存储在ZIP文件的尾部。这个属性表明Z l P 文件支持文件边界处切割,每一个分片中包含ZIP压缩文件中的一个或多个文件。
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 ); } } |
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); |
1
2
|
conf.setCompressMapOutput( true ); conf.setMapOutputCompressorClass(GzipCodec. class ); |
压缩就到此为止了。总之编码和解码在hadoop有着关键的作用。