1. HDFS的数据完整性
HDFS会对写入的所有数据计算校验和,并在读取数据时验证校验和。datanode负责在验证收到的数据后存储数据及其校验和。正在写数据的客户端将数据及其校验和发送到由一系列datanode组成的管线,管线中的最后一个datanode负责验证校验和。如果datanode检测到错误,客户端便会收到一个ChecksumException异常。
客户端从datanode读取数据时,也会验证校验和,将它们与datanode中存储的校验和进行比较。每个datanode均持久保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个datanode,datanode由此更新日志。
不只是客户端在读取数据块时会验证校验和,每个datanode也会在后台线程中运行一个DataBlockScanner,从而定期验证存储在这个datanode上的所有数据块。
客户端在读取数据块时,如果监测到错误,就像namenode报告已损坏的数据块及其正在尝试读操作的这个datanode,最后才抛出ChecksunException异常。namenode将这个已损坏的数据块的复本标记为已损坏,之后安排这个数据块的一个复本复制到另一个datanode,如此一来,数据块的复本因子又回到期望水平。此后,已损坏的数据块复本便被删除。
禁用校验和的方法:在的、使用open()方法读取文件之前,将false值传递给FileSystem对象的setVerifyChecksum()方法。
(1) LocalFileSystem执行客户端的校验和验证,意味着在你写入名为filename的文件时,客户端会在包含每个文件块校验和的同一个目录内新建一个名为filename.crc的隐藏文件。读取文件时需要验证校验和,若检测到错误,LocalFileSystem将抛出ChecksumException异常。
禁用校验和,可使用RawLocalFileSystem代替LocalFileSystem。
(2) ChecksumFileSystem
LocalFileSystem通过ChecksumFileSystem来完成校验,有了该类,向其他文件系统加入校验和就非常简单。
2. 压缩
压缩格式:
所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。表中的压缩工具提供9个不同的选项来控制压缩时间时必须考虑的权衡:-1为优化压缩速度,-9为优化压缩时间。
codec实现了一种压缩-解压缩算法。在Hadoop中,一个对CompressionCodec接口的实现代表一个codec。
(1) 通过CompressionCodec对数据流进行压缩和解压缩
如果要对写入输出数据流的数据进行压缩,可用createOutputStream(OutputStream out)方法新建一个CompressOutputStrean对象,相反,对输入数据流中读取的数据进行解压缩时,调用createInpueStream(InputStream in)获取CompressionInputStream。
public class StreamCompressor{ public static void main(String[] args) { // 将CompressionCodec实现的完全合格名称作为第一个命令行参数 String codecClassname = args[0]; Class<?> codecClass = Class.forName(codecClassname); Configuration conf = new Configuration(); // 使用ReflectionUtils构建一个新的codec实例 CompressionCodec codec = (CompressionCodec)ReflectUtils.newInstance(codecClass,conf); // 在System.out上包裹一个压缩方法。 CompressionOutputStream out = codec.createOutputStream(System.out); IOUtils.copyBytes(System.in,out,4096,false);
// 调用finish()方法,要求压缩方法完成到压缩数据流的写操作,但不关闭这个数据流 out.finish(); } }
例:通过GzipCodec的Stream对象对字符串"Text"进行压缩,然后使用gunzip从标准输入中对它进行读取并解压缩:
% echo "Text" | hadoop StreamCompressor org.apache.hadoop.io.compress.GzipCodec | gunzip
(2) 通过CompressionCodecFactory推断CompressCodec
CompressionCodecFactory的getCodec()方法可以将文件扩展名映射到一个CompressionCodec,该方法去文件Path对象欧威参数。
例:由文件扩展名推断而来的codec对文件进行解压缩
public class FileDecompressor{ public static void main(String[] args) { String uri = args[0]; Configuration conf = new Configuration(); FileSystem fs = FileSyste.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); }
// 一旦找到对应的codec,便去除文件扩展名行成输出文件名 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); } } }
例:一个名为file.gz的文件可以通过下面的程序压缩为名为file的文件:
% hadoop FileDecompressor file.gz
下表为压缩codec的属性:
(3) 原生类库
原生类库可以提供压缩/解压缩性能。注意:并非所有格式都有原生实现。
可以通过Java系统的java.library.path属性指定原生代码库。默认情况下,Hadoop会根据自身运行的平台搜索原生代码库。禁用原生代码库,将属性hadoop.native.lib的值设置成false即可。