第5章 Hadoop的I/O操作
Hadoop 自带一套原子操作用于数据I/O操作。其中一些技术比Hadoop本身更常用,如数据完整性和压缩,但在处理多达好几个TB的数据集时,特别值得关注。其他一些则是Hadoop工具或API,它们所形成的构建模块可用于开发分布式系统,比如系列化框架和在盘(on-disk)数据结构。
HDFS 的数据完整性
HDFS 会对写入的数据计算校验和,并在读取数据时验证校验和。它针对每个由dfs.bytes-per-checksum 指定字节的数据计算校验和。默认情况下为512个字节,由于CRC-32 校验和(HDFS用于校验和和计算的是一个更有效的变体CRC-32C)是4个字节,所以存储校验和的额外开销低于1%。
datanode 负责在收到数据后存储该数据及其校验和之前对数据进行验证。它在收到客户端的数据或复制其他datanode的数据时执行这个操作。正在写数据的客户端将数据及其校验和发送到由一系列datanode组成的管线,管线中最后一个datanode负责验证校验和。如果datanode检测到错误,客户端便会收到一个IOException异常的一个子类,对于该异常应以应用程序特定的方式来处理,比如重试这个操作。
客户端从datanode读取数据时,也会验证校验和,将它们与datanode中存储的校验和进行比较。每个datanode均持久保存有一个用于验证的校验和日志(persistent log of checksum verification),所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个datanode,datanode由此更新日志。保存这些统计信息对于检测损坏的磁盘很有价值。
不只是客户端在读取数据块时会验证校验和,每个datanode 也会在一个后台线程中运行一个DataBlockScanner,从而定期验证存储在这个datanode上的所有数据块。该项措施是解决物理存储媒体上位损坏的有力措施。
由于HDFS 存储着每个数据块的复本(replica),因此它可以通过数据复本来修复损坏的数据块,进而得到一个新的、完好无损的复本。基本思路是,客户端在读取数据块时,如果检测到错误,首先向namenode报告已损坏的数据块及其正在尝试读操作的这个datanode,再抛出ChecksumException异常。namenode将这个数据块复本标记为已损坏,这样它不再将客户端处理请求直接发送到这个节点,或尝试将这个复本复制到另一个datanode。之后,它安排这个数据块的一个复本复制到另一个datanode,如此一来,数据块的复本因子(replica factor)又回到期望水平。此后,已损坏的数据块复本便被删除。
可通过Hadoop的命令 fs -checksum 来检查一个文件的校验和。distcp命令亦可。
当你向HDFS写入一个名为filename的文件时,文件系统客户端会明确在包含每个文件块校验和的同一个目录内新建一个 .filename.crc 隐藏文件。文件块的大小由属性 file.bytes-per-checksum 控制,默认为512 字节。文件块的大小作为元数据存储在 .crc 文件中,所以即使文件块大小的设置已经发生变化,仍然可以正确读回文件。
校验和的计算代价是相当低的(在Java中,它们是用本地代码实现的),一般只是增加少许额外的读/写文件时间。对大多数应用来说,付出这样的额外开销以保证数据完整性是可以接受的。此外,我们也可以禁用校验和计算,特别是在底层文件系统本身就支持校验和的时候。
压缩
文件压缩有两大好处:减少存储文件所需要的磁盘空间,并加速数据在网络和磁盘上的传输。这两大好处在处理大量数据时相当重要。
所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。
gzip -1 file – 通过最快的压缩方法创建一个名为file.gz的压缩文件,但压缩后文件占用空间最大
gzip -9 file – 压缩后文件占用空间最小,但压缩和解压缩最慢。
压缩和输入分片
在考虑如何压缩将由 Mapreduce处理的数据时,理解这些压缩格式是否支持切分(splitting)是非常重要的。如果一个文件是经过gzip 压缩的,且压缩后文件大小为 1 GB。如果每个 HDFS 的块大小设置为128 MB,那么 HDFS 将这个文件保存为8个数据块。但是,将每个数据块单独作为一个输入分片是无法实现的,因为无法实现从gzip压缩数据流的任意位置读取数据(gzip不支持切分),所以让map任务独立于其他任务进行数据读取是行不通的。
在这种情况下,Mapreduce 不会尝试切分gzip压缩文件,这是可行的,但牺牲了数据本地性:一个map任务处理8个HDFS块,而其中大多数块并没有存储在执行该map任务的节点上。而且,map任务数越少,作业的粒度就较大,因而运行时间会更长。
对于大文件来说,不要使用不支持切分的压缩格式,因为会失去数据的本地特性,进而造成Mapreduce应用效率低下。
Parquet是一个基于Google Dremel的通用的面向列的文件格式。