数据完整性
IO操作过程中难免会出现数据丢失或脏数据,数据传输量越大出错的几率就越大。校验错误最常用的办法就是传输前计算一个校验和,传输后计算一个校验和,两个校验和如果不相同就说明数据存在错误,比较常用的错误校验码是CRC32。
HDFS数据完整性
HDFS写入的时候计算出校验和,然后每次读的时候再计算校验和。要注意的一点就是,HDFS每固定长度就会计算一次校验和,这个值由io.bytes.per.checksum指定,默认值是512字节。因为CRC32是32位即4个字节,这样校验和占用的空间就会少于原数剧1%。1%这个数字在hadoop中会经常看到。以后有时间会整理一份和1%不得不说的故事。
DataNode在存储收到的数据前会校验数据的校验和,比如收到客户端的数据或者其他副本传过来的数据。客户端写入数据到HDFS时的数据流,在管道的最后一个DataNode会去检查这个校验和,如果发现错误,就会抛出ChecksumException到客户端。
客户端从DataNode读取数据的时候一样要检查校验和,而且每个DataNode还保存了检查校验和的日志,客户端的每一次校验都会记录到日志中。
除了读写操作会检查校验和以外,DataNode还跑着一个后台进程(DataBlockScanner)来定期校验存在在它上面的的block,因为除了读写过程中会产生数据错误以外,硬件本身也会产生数据错误,比如说位衰减(bit rot)
如果客户端发现有block坏掉,主要通过以下步骤进行修复:
1.客户端在抛出ChecksumException之前会把坏的block和block所在的DataNode报告给NameNode。
2.NameNode把这个block标记为已损坏,这样NameNode就不会把客户端指向这个block,也不会复制这个block到其他DataNode。
3.NameNode会把一个好的block复制到另外一个DataNode。
4.NameNode把坏的block删除掉。
注:如果出于一些原因在操作的时候不想让HDFS检查校验码,在调用FileSystem的open方法前调用setVerityChecksum方法,并设置为false即可,命令行下可以使用-ignoreCrc参数。
实现
LocalFileSystem继承自ChecksumFileSystem,已经实现了checksum的功能,checksum的信息存储在与文件同名的crc文件中,发现错误的文件放在bad_files文件夹中。如果你确认顶层系统已经实现了checksum功能,那么你就没有必要使用LocalFileSystem,改为使用RowLocalFileSystem,可以通过更改fs.file.impl=org.apache.hadoop.fs.RawLocalFileSystem全局指定,也可以通过代码直接实例化。
Configuration conf=...
FileSystem fs=new RawLocalFileSystem();
fs.initialize(null, conf);
如果其他的FileSystem想拥有checksum的功能,只需要用ChecksumFileSystem包装一层即可:
FileSystem rawFs=...
FileSystem checksummedFs=new ChecksumFileSystem(fs){} ;