HADOOP自带了一组原始的数据I/O。其中一些技术比HADOOP更通用,如数据完整性和压缩,
但在处理TB级的数据时需要特别考虑。其它的HADOOP工具或API是开发分布式系统的构建块,如
序列化框架和磁盘上的数据结构。
数据完整性
HADOOP用户期望在存储或处理数据时没有丢失或损坏。然而,每一个磁盘或网络的读写的IO
操作都可能导致数据错误,当流经系统的海量数量和HADOOP的处理极限一样大的,数据损坏的
机会是很高的。
通常的数据校验方法是在数据第一次进入系统时计算数据的校验和,当它在一个不可信的通道
传播时有可能导致数据损坏会再次校验。如果新生成的数据的检验和与原始的不能精确匹配,那么就
认为它是损坏的。这个技术没有提供修复数据的方法----它仅仅检测错误(这是为什么不使用低端硬件
的原因;尤其是要使用ECC内存)。注意,有可能是校验和损坏而不是数据损坏,但是这个机率很小,
因为校验和比数据小的多。
一个普遍使用的检测错误的是CRC-32(32-bit cyclic redundancy check),它对任意大小的输入
产生一个32位的校验和。CRC-32用在HADOOP的ChecksumFileSystem中用来做校验和处理,同时
HDFS使用一个更有效的变体叫CRC-32C。
HDFS中的数据完整性
HDFS校验所有写入/读取的数据。每dfs.bytes-perchecksum的byte数据会产生一个单独的检验和。
默认的是512byte,因为CRC-32C校验和长度是4byte,存储开销小于1%。
datanode在接收数据存储之前,负责检查数据及校验。这适用于他们从客户端接收的数据和在复制
时从其它datanode接收的数据。客户端把数据发送到datanode组成的pipeline(第三章解释过),pipeline
中的最后一个datanode检查校验和。如果这个datanode检测到错误,客户端接收到一个IOException的
子类,需要它处理(例如,重新执行操作)。
当客户端从datanode读取数据时,它们也会检查校验和,把它与存储了这个数据的datanode比较。
每一个datanode都保存了校验和验证日志,所以它知道它的每一个block的最后验证时间。当客户端验证
成功一个block,它告诉datanode,datanode会更新它的日志。像这样保存统计信息在检测损坏磁盘时
很有价值。
除了客户端读取时会检验block之外,每一个datanode运行一个DataBlockScanner,它会周期性检查
datanode中所存储的所有block。这样做是为了防止物理存储媒介的位衰减。
由于HDFS存储了block的副本,它可以通过拷贝好的副本来“恢复”损坏的block。它的工作方式是如果
客户端读取block时发现一个错误,再抛出一个ChecksumException之前,它会把试图从namenode读取的
block和datanode报告给namenode,namenode标记这个block为损坏的,这样它不会再把客户端定位到
它或把它拷贝到别的datanode。然后它从其它的datanode上拷贝一个副本,这样复制因子回复到期望水平。
一旦复制完毕,删除损坏的副本。
在使用FileSystem的open()方法之前,可以通过传递false给setVerifyChecksum()方法来禁止检验和检查。
在shell中,在使用-get或-copyToLocal命令时,同样可以通过使用-ignoreCrc选项来达到同样的效果。这个
特性在你想要检查损坏文件来决定如何处理它是有用。例如,你可能希望在删除之前查看它是否还能恢复。
你可以使用hadoop fs -checksum来查看一个文件的校验和。这在查看HDFS中的两个文件内容是否一样
时很有用----例如,distcp会做这些事。
LocalFileSystem
HADOOP 的LocalFileSystem执行客户端的校验。这意味着当你写一个名叫filename的文件,客户端文件系统
会创建一个文件,filename.crc,与包含文件每一块的校验和同一个目录。块的大小由file.bytes-per-checksum属性
控制,默认值是512byte。块的大小做为.crc文件的一个元数据保存在.crc文件中,所以如果改变了块的大小,这个
文件还是可以正确读取。当读取文件时,会检查校验和,如果有错误,LocalFileSystem会抛出一个ChecksumException。
计算校验和耗费很少(在JAVA中,他们是用native代码实现),读写文件时一般会增加百分之几的耗时。对于
大部分应用,为了确保数据完整性这个代价是可以接受的。也可以禁止校验和验证,这样做一般是底层文件系统本身
支持校验和检查。可以使用RawLocalFileSystem代替LocalFileSystem来完成。为了在整个应用中达到这个目的,可以
重新映射file URI的实现,设置fs.file.impl的属性值为org.apache.hadoop.fs.RawLocalFileSystem。或者,你可以直接
创建一个RawLocalFileSystem实例,在你只想禁止校验某些读取时有用,例如:
Configuration conf = ...
FileSystem fs = new RawLocalFileSystem();
fs.initialize(null, conf);
ChecksumFileSystem
LocalFileSystem使用ChecksumFileSystem做它的工作,这个类给其它不支持校验和的文件系统添加校验和功能很容易,
因为ChecksumFileSystem只是FileSystem的一个包装。一般程式如下:
FileSystem rawFs = ...
FileSystem checksummedFs = new ChecksumFileSystem(rawFs);
底层的文件系统叫raw文件系统,可以通过ChecksumFileSystem的getRawFileSystem()方法得到。ChecksumFileSystem
有几个更有用的方法,如用来得到任意文件的校验和文件的方法getChecksumFile()。
当ChecksumFileSystem读取文件时发现一个错误,它会调用它的reportChecksumFailure()方法。默认的实现不做任何事,
但LocalFileSystem会把损坏文件及它的校验和移动到同一个设备上的bad_files文件夹。管理员应该周期性检查这些文件并
对它采取行动。