文件压缩可以降低存储需要的空间,并且在传输过程中加快传输速度。因此对于大量数据的处理时,压缩是十分重要的。我们考虑一下Hadoop在文件中的压缩用法。
有许多压缩方式,如下:
压缩格式 | 工具 | 算法 | 文件拓展名 | 是否可切分 |
---|---|---|---|---|
DEFLATE | 无 | DEFLATE | .deflate | 否 |
Gzip | gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | bzip2 | bz2 | 是 |
LZO | lzop | LZO | .lzo | 否 |
LZ4 | 无 | LZ4 | .lz4 | 否 |
Snappy | 无 | Snappy | .snappy | 否 |
压缩主要考虑时间和速度,在上面的三种压缩工具中都提供了对压缩时间和压缩空间的调整参数。-1是为优化压缩速度,-9是优化压缩空间,1~9中间其他的参数介于二者之间。
另外不同的压缩有着不同的特性,
1. 使用CompressionCodec接口实现压缩和解压缩
在Hadoop中CompressionCodec接口定义了压缩和解压缩的方法。其包含许多具体的实现。
实现如下:
压缩格式 | 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中包含两个函数用于压缩和解压:
createOutputStream(OutputStream out)
压缩out中的内容,并返回一个CompressionOutputStream对象来包含压缩结果。createInputStream(InputStream in)
解压in中的内容,并返回一个CompressionInputStream对象来包含解压结果。2. 压缩程序
编写一个压缩程序:
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
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.CompressionOutputStream;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.util.ReflectionUtils;
public class GZipCodec {
public static void main(String[] args) throws IOException {
String srcUrl = args[0];
String targetUrl = args[1];
Configuration conf = new Configuration();
FileSystem inFs = FileSystem.get(URI.create(srcUrl),conf);
FileSystem outFs = FileSystem.get(URI.create(targetUrl),conf);
CompressionCodec codec = ReflectionUtils.newInstance(GzipCodec.class,conf);
FSDataInputStream inputStream = inFs.open(new Path(srcUrl));
CompressionOutputStream outputStream = codec.createOutputStream(outFs.create(new Path(targetUrl)));
IOUtils.copyBytes(inputStream, outputStream, 4096,false);
outputStream.finish();
IOUtils.closeStream(outputStream);
IOUtils.closeStream(inputStream);
System.out.println("输入文件的大小"+inFs.getFileStatus(new Path(srcUrl)).getLen()+"b");
System.out.println("输出文件的大小"+outFs.getFileStatus(new Path(targetUrl)).getLen()+"b");
System.out.printf("压缩率为%.2f%%\n",100*(1.0*outFs.getFileStatus(new Path(targetUrl)).getLen()/inFs.getFileStatus(new Path(srcUrl)).getLen()));
}
}
在hadoop下运行:
[grid@tiny01 input]$ hadoop GZipCodec file:///home/grid/input/data.txt file:///home/grid/input/data.gz
17/07/28 01:49:03 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java
classes where applicable
输入文件的大小49252b
输出文件的大小2362b
压缩率为4.80%
可以看到压缩的还是很不错的。我们使用linux的gzip命令压缩一下
[grid@tiny01 input]$ md5sum data.txt
e8d882c5b4f4c8d6952d8a88a4d65c52 data.txt
[grid@tiny01 input]$ gzip -d data.gz
[grid@tiny01 input]$ md5sum data
e8d882c5b4f4c8d6952d8a88a4d65c52 data
我们可以看到压缩的和解压的是同一个文件。(md5sum在cyrus-sasl-md5包下)
关于
17/07/28 01:49:03 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
是因为Java默认查询原生类库(native),如果找到则会自动加载,如果没有找到就会出现这个警告。
3. 解压程序
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
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.CompressionInputStream;
import org.apache.hadoop.io.compress.CompressionOutputStream;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.util.ReflectionUtils;
public class GZipDCodec {
public static void main(String[] args) throws IOException {
String srcUrl = args[0];
String targetUrl = args[1];
Configuration conf = new Configuration();
FileSystem inFs = FileSystem.get(URI.create(srcUrl),conf);
FileSystem outFs = FileSystem.get(URI.create(targetUrl),conf);
CompressionCodec codec = ReflectionUtils.newInstance(GzipCodec.class,conf);
FSDataOutputStream outputStream = outFs.create(new Path(targetUrl));
CompressionInputStream inputStream = codec.createInputStream(inFs.open(new Path(srcUrl)));
IOUtils.copyBytes(inputStream, outputStream, 4096,false);
outputStream.close();
inputStream.close();
System.out.println("输入文件的大小"+inFs.getFileStatus(new Path(srcUrl)).getLen()+"b");
System.out.println("输出文件的大小"+outFs.getFileStatus(new Path(targetUrl)).getLen()+"b");
System.out.printf("解压率为%.2f%%\n",100*(1.0*outFs.getFileStatus(new Path(targetUrl)).getLen()/inFs.getFileStatus(new Path(srcUrl)).getLen()));
}
}
运行可得
[grid@tiny01 input]$ ll
总用量 64
-rw-r--r--. 1 grid grid 2362 7月 28 04:55 data.gz
-rw-rw-r--. 1 grid grid 49252 7月 27 23:46 data.txt
-rw-r--r--. 1 grid grid 8 7月 27 04:03 word2.txt
-rw-rw-r--. 1 grid grid 11 6月 25 18:31 word.txt
[grid@tiny01 input]$ hadoop GZipDCodec file:///home/grid/input/data.gz file:///home/grid/input/data1.txt
17/07/28 16:34:01 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/07/28 16:34:01 INFO compress.CodecPool: Got brand-new decompressor [.gz]
输入文件的大小2362b
输出文件的大小49252b
解压率为2085.18%
[grid@tiny01 input]$ ll
总用量 116
-rw-r--r--. 1 grid grid 49252 7月 28 16:34 data1.txt
-rw-r--r--. 1 grid grid 2362 7月 28 04:55 data.gz
-rw-rw-r--. 1 grid grid 49252 7月 27 23:46 data.txt
-rw-r--r--. 1 grid grid 8 7月 27 04:03 word2.txt
-rw-rw-r--. 1 grid grid 11 6月 25 18:31 word.txt
4.根据文件拓展名推测解压方式
hadoop还提供了一个通过拓展名猜测压缩算法的方法:CompressionCodecFactory类中的getCodec()方法。
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
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;
public class DefaultDCodec {
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("没有找到压缩方式:"+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);
} catch (Exception e) {
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
}
}
运行结果:
[grid@tiny01 input]$ ll
总用量 64
-rw-r--r--. 1 grid grid 2362 7月 28 04:55 data.gz
-rw-rw-r--. 1 grid grid 49252 7月 27 23:46 data.txt
-rw-r--r--. 1 grid grid 8 7月 27 04:03 word2.txt
-rw-rw-r--. 1 grid grid 11 6月 25 18:31 word.txt
[grid@tiny01 input]$ hadoop DefaultDCodec file:///home/grid/input/data.gz
17/07/28 21:48:19 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/07/28 21:48:20 INFO compress.CodecPool: Got brand-new decompressor [.gz]
[grid@tiny01 input]$ ll
总用量 116
-rw-r--r--. 1 grid grid 49252 7月 28 21:48 data
-rw-r--r--. 1 grid grid 2362 7月 28 04:55 data.gz
-rw-rw-r--. 1 grid grid 49252 7月 27 23:46 data.txt
-rw-r--r--. 1 grid grid 8 7月 27 04:03 word2.txt
-rw-rw-r--. 1 grid grid 11 6月 25 18:31 word.txt
5.参考资料
[1] Hadoop:The Definitive Guide,Third Edition, by Tom White. Copyright 2013 Tom White,978-1-449-31152-0