《Hadoop海量数据处理:技术详解与项目实战》一 3.2 HDFS读取文件和写入文件

    xiaoxiao2024-04-18  9

    本节书摘来异步社区《Hadoop海量数据处理:技术详解与项目实战》一书中的第3章,第3.2节,作者: 范东来 责编: 杨海玲,更多章节内容可以访问云栖社区“异步社区”公众号查看。

    3.2 HDFS读取文件和写入文件

    Hadoop海量数据处理:技术详解与项目实战我们知道在HDFS中,NameNode作为集群的大脑,保存着整个文件系统的元数据,而真正数据是存储在DataNode的块中。本节将介绍HDFS如何读取和写入文件,组成同一文件的块在HDFS的分布情况如何影响HDFS读取和写入速度。

    3.2.1 块的分布HDFS会将文件切片成块并存储至各个DataNode中,文件数据块在HDFS的布局情况由NameNode和hdfs-site.xml中的配置dfs.replication共同决定。dfs.replication表示该文件在HDFS中的副本数,默认为3,即有两份冗余。

    图3-5为dfs.replication为1的分布情况,即没有冗余。图3-6为dfs.replication为2的分布情况,即有一份冗余。

    图像说明文字

    NameNode如何选择在哪个DataNode存储副本?这里需要在可靠性、写入速度和读取速度之间进行一些取舍。如果将所有副本都存储在一个节点之中,那么写入的带宽损失将是最小的,因为复制管道都是在单一节点上运行。但这样无论副本数设为多少,HDFS都不提供真实的冗余,因为该文件的所有数据都在一个节点之上,那么如果该节点发生故障的话,该文件的数据将会丢失。如果将副本放在不同数据中心,可以最大限度地提高冗余,但是对带宽的损耗非常大。即使在同一数据中心,也有不同的副本分布策略。其实,在发布的Hadoop 0.17.0版本中改变了数据布局策略来辅助保持数据块在集群内分布相对均匀。从0.21.0版本开始,可即时选择块的分布策略。

    Hadoop的默认布局是在HDFS客户端节点上放第一个副本,但是由于HDFS客户端有可能运行于集群之外,就随机选择一个节点,不过Hadoop会尽量避免选择那些存储太满或者太忙的节点。第二个副本放在与第一个不同且随机另外选择的机架中的节点上。第三个副本与第二个副本放在相同的机架,且随机选择另外一个节点。其他副本(如果dfs.replication大于3)放在集群随机选择的节点上,Hadoop也会尽量避免在相同的机架上放太多副本。当NameNode按照上面的策略选定副本存储的位置后,就会根据集群网络拓扑图创建一个管道。假设dfs.replication = 3,则如图3-7所示。

    这样的方法不仅提供了很好的数据冗余性(如果可能,块存储在两个机架中)并实现很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(可以从两个机架中进行选择读取)和集群中块的均匀分布。

    3.2.2 数据读取

    HDFS客户端可以通过多种不同的方式(如命令行、Java API等)对HDFS进行读写操作,这些操作都遵循同样的流程。HDFS客户端需要使用到Hadoop库函数,函数库封装了大部分与NameNode和DataNode通信相关的细节,同时也考虑了分布式文件系统在诸多场景的错误处理机制。

    假设在HDFS中存储了一个文件/user/test.txt,HDFS客户端要读取该文件,Hadoop客户端程序库是必不可少的。如图3-8所示,HDFS客户端首先要访问NameNode,并告诉它所要读取的文件,在这之前,HDFS会对客户的身份信息进行验证。验证的方式有两种:一种是通过信任的客户端,由其指定用户名;第二种方式是通过诸如Kerberos等强制验证机制来完成。接下来还需要检查文件的所有者及其设定的访问权限。当文件确实存在,且该用户对其有访问权限,这时NameNode会告诉HDFS客户端这个文件的第一个数据块的标号以及保存有该数据块的DataNode列表。这个列表是DataNode与HDFS客户端间的距离进行了排序。有了数据块标号和DataNode的主机名,HDFS客户端便可以直接访问最合适的DataNode,读取所需要的数据块。这个过程会一直重复直到该文件的所有数据块读取完成或HDFS客户端主动关闭了文件流。特殊的情况,如果该HDFS客户端是集群中的DataNode时,该节点将从本地DataNode中读取数据。

    3.2.3 写入数据

    HDFS的数据写操作相对复杂些,以HDFS客户端向HDFS创建一个新文件为例,如图3-9所示。

    首先HDFS客户端通过HDFS相关API发送请求,打开一个要写入的文件,如果该用户有写入文件的权限,那么这一请求将被送达NameNode,并建立该文件的元数据。但此时新建立的文件元数据并未和任何数据块相关联,这时HDFS客户端会收到“打开文件成功”的响应,接着就可以写入数据了。当客户端将数据写入流时,数据会被自动拆分成数据包,并将数据包保存在内存队列中。客户端有一个独立的线程,它从队列中读取数据包,并向NameNode请求一组DataNode列表,以便写入下一个数据块的多个副本。接着,HDFS客户端将直接连接到列表中的第一个DataNode,而该DataNode又连接到第二个DataNode,第二个又连接到第三个,如此就建立了数据块的复制管道。复制管道中的每一个DataNode都会确认所收到的数据包已经成功写入磁盘。HDFS客户端应用程序维护着一个列表,记录着哪些数据包尚未收到确认信息。每收到一个响应,客户端便知道数据已经成功地写入管道中的一个DataNode。当数据块被写入列表中的DataNode中时,HDFS客户端将重新向NameNode申请下一组DataNode。最终,客户端将剩余数据包写入全部磁盘,关闭数据管道并通知NameNode文件写操作已经完成。

    如果写入的时候,复制管道中的某一个DataNode无法将数据写入磁盘(如DataNode死机)。发生这种错误时,管道会立即关闭,已发送的但尚未收到确认的数据包会被退回到队列中,以确保管道中错误节点的下游节点可以得到数据包。而剩下的健康的DataNode中,正在写入的数据块会被分配新的blk_id。这样,当发生故障的数据节点恢复后,冗余的数据块就会因为不属于任何文件而被自动丢弃,由剩余DataNode节点组成的新复制管道会重新开放,写入操作得以继续,写操作将继续直至文件关闭。NameNode如果发现文件的某个数据块正在通过复制管道进行复制,就会异步地创建一个新的复制块,这样,即便HDFS的多个DataNode发生错误,HDFS客户端仍然可以从数据块的副本中恢复数据,前提是满足最少数目要求的数据副本(dfs.replication.min)已经被正确写入(dfs.replication.min配置默认为1)。

    3.2.4 数据完整性

    Hadoop用户都希望HDFS在读写数据时,数据不会有任何丢失或者损坏。但是在实际情况中,如果系统需要处理的数据量超过HDFS能够处理的极限,那么数据被损坏的概率还是很高的。

    检测数据是否损坏的常用措施是,在数据第一次引入系统时计算校验和,并在数据通过一个不可靠的通道进行数据传输时再次计算校验和,如果发现两次校验和不一样,那么可以判定,数据已经损坏。校验和技术并不能修复数据,只能检测出数据是否已经损坏1。

    HDFS也是采用校验和技术判断数据是否损坏,HDFS会对写入的所有数据计算校验和,并在读取数据的时验证校验和,它针对由core-site.xml文件的io.bytes.per.checksum配置项指定字节的数据计算校验和,默认为512字节。

    DataNode负责验证收到的数据的校验和,并且如果该校验和正确,则保存收到的数据。DataNode在收到客户端的数据或复制其他DataNode的数据时执行这个操作。正在写数据的HDFS客户端将数据及其校验和发送到由一系列DataNode组成的复制管道,如图3-9所示,管道中最后一个DataNode负责验证校验和。如果DataNode检测到错误,HDFS客户端便会收到一个校验和异常,可以在应用程序中捕获该异常,并做相应的处理,例如重新尝试写入。

    HDFS客户端从DataNode读取数据时,也会验证校验和,将它们与DataNode中存储的校验和进行比较。每个DataNode均保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个DataNode,DataNode由此更新日志。

    不只是客户端在读取数据和写入数据时会验证校验和,每个DataNode也会在一个后台线程运行一个DataBlockScanner,定期验证存储在这个DataNode上的所有数据块。

    由于HDFS存储着每个数据块的副本,因此当一个数据块损坏时,HDFS可以通过复制完好的该数据块副本来修复损坏的数据块,进而得到一个新的、完好无损的副本。大致的步骤是,HDFS客户端在读取数据块时,如果检测到错误,则向NameNode报告已损坏的数据块以及尝试读取该数据块的DataNode,最后才抛出ChecksumException异常。NameNode将这个已损坏的数据块标记为已损坏。之后,它安排这个数据块的一个副本复制到另一个DataNode,如此一来,数据块的副本数又回到了配置的水平。最后,已损坏的数据块副本便会被删除。

    相关资源:步进工作过程如上图所示
    最新回复(0)