目录
1. HDFS简介
2. HDFS基本操作
3. HDFS原理
1. HDFS简介
HDFS概念:
HDFS是一个分布式的文件系统。分布式意味着多台机器存储,文件系统,就是用来存储文件、存储数据。是大数据最底层一个服务。
HDFS设计目标:
- 故障的检测和自动快速恢复是HDFS的核心架构目标。
- 面对海量数据的存储,注重吞吐能力,而不是交互式。
- 支持大文件存储。
- 一次写入多次读取。
- 移动计算的代价比之移动数据的代价低。一个应用请求的计算,离它操作的数据越近就越高效,这在数据达到海量级别的时候更是如此。将计算移动到数据附近,比之将数据移动到应用所在显然更好。
- 在异构的硬件和软件平台上的可移植性。
HDFS特性:
- HDFS是主从架构:主角色是 namenode ,其管理维护着元数据,如目录树的结构 文件的大小、文件的副本备份、文件的位置信息等;从角色是 datanode,其存储着最终的数据块。
- 分块存储:物理上把文件分为一个一个块(block),默认一个块大小在hadoop2.x版本中是128M,也可以通过参数手动设置。比如1个文件300MB,第一块0-128MB,第二块128-256MB,第三块256-300MB。
- 副本机制:为了提高容错性,每个block都在HDFS中都会有副本,副本数量也可以通过参数设置dfs.replication,默认是3。本身1个,副本2个,一共三个。
- 名字空间(命名空间)(namespace):也就是支持传统的层次型文件组织结构,也就是目录树。用户可以创建、删除、移动或重命名文件。Namenode负责维护namespace,任何对namespace的修改都将被Namenode记录下来。
- metadata(元数据):元数据也叫解释性数据,就是记录数据的数据,对于HDFS来说,目录结构及文件分块位置信息叫做元数据,元数据是由namenode维护的。
- datanode(数据存储):文件的各个block的具体存储管理由datanode节点承担,每一个block都可以在多个datanode上,datanode需要定时向Namenode汇报自己持有的block信息。
- write one read many(一次写入多次读取):这是由设计目标决定的,不支持文件的修改,正因为如此,HDFS适合用来做大数据分析的底层存储服务。
2. HDFS基本操作
命令行使用:
hadoop fs -ls hdfs://node1:8020/...
hadoop fs可以操作的文件系统不仅仅有HDFS,还包括本地文件系统、GFS、TFS等。
比如本地:hadoop fs -ls file:///...
如果直接hadoop fs -ls / ,其访问的文件系统取决于参数fs.defaultFS。默认为file:///
常见命令操作:
hadoop fs -ls [-h] [-R] <args> :查看指定目录下信息,其中-h人性化显示,-R 递归显示。
例:hadoop fs -ls -h hdfs://node1:8020/
hadoop fs -mkdir [-p] <paths>:在hdfs上创建目录,-p表示会创建路径中的各级父目录
hadoop fs -put [-f] [-p] src dst:将单个或多个本地文件复制到目标文件系统,src代表的是本地目录,dst代表的是HDFS。其中-p:保留访问和修改时间,所有权和权限 。-f:覆盖目的地(如果已经存在)。例:hadoop fs -put file:///root/aaa.txt hdfs://node1:8020/aaa,表示将本地的aaa.txt上传到HDFS的aaa目录下。
hadoop fs -get [-ignorecrc] [-crc] [-p] [-f] src localdst:将文件下载到本地文件系统,其中-ignorecrc表示跳过对下载文件的CRC检查,-crc表示为下载的文件写CRC校验和。
hadoop fs -appendToFile <localsrc> ... <dst>:追加一个文件到已经存在的文件末尾,把本地的小文件上传合并成为大文件 解决小文件场景。
hadoop fs -cat [-ignoreCrc] URI [URI ...]:显示文件内容到stdout。
hadoop fs -tail [-f] URI:将文件的最后一千字节内容显示到stdout,-f选项将在文件增长时输出附加数据。
hadoop fs -chgrp [-R] GROUP URI [URI ...]:修改所属组,用户必须是文件的所有者,或者是超级用户,-R将使改变在目录结构下递归进行。
-chmod :改变文件的权限,-R将使改变在目录结构下递归进行。
-chown: 修改文件拥有者。
-cp:从hdfs的一个路径拷贝hdfs的另一个路径。
-mv:在hdfs目录中移动文件。
-rm:删除指定的文件,只删除非空目录和文件,-r 递归删除。
-getmerge:合并下载多个文件 其功能和appendToFile相反的动作。
hadoop fs -df -h / :统计HDFS可用空间 指定目录大小。
hadoop fs -setrep -w N -R :修改文件的副本数,N表示修改之后的副本数,-w表示修改副本客户端是否等待修改完毕再推出-R选项用于递归改变目录下所有文件的副本系数,例如:hadoop fs -setrep 2 /aaa/aaa.txt表示修改aaa.txt副本数为2。
3. HDFS原理
Namenode
NameNode 是 HDFS 的核心组件,充当 HDFS 的主节点,主要负责存储文件系统的元数据,即 HDFS 文件目录结构,并记录集群中每个文件的块信息及其位置。然而,NameNode 并不存储实际的数据内容,这些数据块保存在 DataNode 中。通过元数据,NameNode 能够知道每个文件对应的数据块以及如何从这些块中重建文件。需要注意的是,NameNode 并不会持久化保存每个数据块在 DataNode 中的具体位置信息,这些位置信息会在系统启动时由 DataNode 动态重建。如果 NameNode 停止运行,HDFS 或整个 Hadoop 集群将不可用,因此它是 Hadoop 集群的单点故障节点。由于 NameNode 的角色至关重要,运行它的服务器通常需要配置较大的内存资源。
Datanode
DataNode 是 HDFS 中负责存储实际数据的从节点,与 NameNode 持续通信。每当 DataNode 启动时,它会向 NameNode 注册并报告自身所管理的数据块信息。即使某个 DataNode 出现故障,也不会对集群或数据的整体可用性造成影响,此时 NameNode 会调度其他 DataNode 创建相应数据块的副本。因为 DataNode 是存储实际数据的核心组件,其运行服务器通常需要配置大容量的硬盘存储空间。此外,DataNode 会定期向 NameNode 发送心跳信号,默认间隔为 3 秒,可通过参数 dfs.heartbeat.interval
进行调整。如果 NameNode 在较长时间内未收到某个 DataNode 的心跳信号,就会将其标记为失效节点。同时,DataNode 会按默认 6 小时的间隔向 NameNode 报告数据块信息,这一间隔可通过参数 dfs.blockreport.intervalMsec
配置。
HDFS写数据流程:
- 客户端请求上传文件。客户端(client)通过 RPC 与 NameNode 建立连接,向其发送文件上传请求。NameNode 检查目标文件是否已存在以及父目录是否有效,随后返回是否允许上传的结果。
- 请求分配第一个 Block 的 DataNode。客户端向 NameNode 请求第一个数据块(block)应存储到哪些 DataNode 节点上。
- NameNode 分配 DataNode。NameNode 根据配置文件中的副本数量及副本放置策略进行分配,返回可用的 DataNode 地址列表。默认副本分配策略由 BlockPlacementPolicyDefault 类实现,3 副本策略如下:第一副本若写入方所在机器是某个 DataNode,则直接存储在本地,否则随机选择一个 DataNode。第二副本存储于不同于第一副本的机架上的一个 DataNode。第三副本:存储在第二副本所在机架上的另一个不同节点。
- 客户端与第一台 DataNode 建立连接。客户端向返回的第一台 DataNode(如 A)发起上传请求(本质是 RPC 调用),同时由 A 继续调用 B,B 再调用 C,逐步建立整个数据传输管道(pipeline)。管道建立完成后,各节点依次将信息返回给客户端。
- 客户端开始上传数据。客户端将数据从磁盘读取并存入本地内存缓存中,按照固定大小的数据包(packet)(默认 64KB)逐个传输到 DataNode A。 A 接收到一个 packet 后,会立即将其传给 B,B 再传递给 C,完成流水线式传输。 A 每传递一个 packet,会将其放入应答队列中,等待来自下游节点的确认。
- 数据传输确认(ACK)。数据包通过管道完成传输后,在管道的反方向上依次发送 ack(确认应答),最终由第一个节点 A 将完整的 pipeline ack 发送给客户端,确认该 packet 已成功写入。
- 重复传输后续数据块。当一个数据块(block)传输完成后,客户端再次向 NameNode 请求分配新的 DataNode 地址用于存储下一个数据块。该过程重复,直到文件所有数据块上传完成。
可以把应答队列理解成一个“任务清单”:
- 节点 A 在完成一个任务(发送数据包到管道的其他节点)之前,会把这个任务记录在清单上。
- 只有当下游节点(B 和 C)完成任务并返回确认,A 才会把任务从清单上划掉。
- 如果下游节点中途出问题,A 的清单会提醒自己需要重新处理这个任务。
HDFS读数据流程:
- 客户端请求文件信息。当我们要读取一个文件时,客户端首先会向 HDFS 的“指挥官”——NameNode 发出请求,问:“这个文件的各个部分(块)存在哪里?”
- NameNode告诉客户端块的位置。NameNode 负责整个文件系统的管理,它知道文件被切分成了哪些块(block),每个块存储在哪些服务器(DataNode)上。NameNode会返回文件的部分或全部块信息,并告诉客户端这些块分别在哪些 DataNode 上。包括副本。
- 客户端对 DataNode 进行排序。客户端拿到块所在 DataNode 的地址列表后,会对这些地址进行排序,排序规则是:谁离我最近谁优先:HDFS根据网络拓扑结构判断哪个 DataNode 离客户端最近(比如同一个机架上的节点会优先)。谁状态更好谁优先:如果某个 DataNode 状态不太好(比如长时间没有心跳响应),它会被排到最后。
- 开始读取数据。客户端从排在最前面的 DataNode 开始读取块数据。如果幸运的话,客户端自己就是其中一个存储块的 DataNode,那么它会直接从本地读取数据。
- 数据读取的底层机制。数据的读取是通过一个叫 FSDataInputStream 的工具完成的。它会反复调用一个叫 read 的方法,一点一点地把数据从块中取出来,直到把这个块读完。
- 读取下一个块。当一个块的数据读完之后,客户端会断开与当前 DataNode 的连接,并转而去读取下一个块所在的 DataNode。这样循环下去,直到把所有块的数据都读完。
- 大文件怎么读?如果文件特别大,可能 NameNode 一开始只给了客户端一部分块的信息。读完这些块后,客户端会再次向 NameNode 请求剩余块的位置,继续读取,直到整个文件读完为止。
- 数据完整性校验。每次读完一个块,客户端都会进行校验(checksum),确保数据没有损坏。如果某个块读取失败(比如 DataNode 挂了),客户端会告诉 NameNode,并从另一个有该块副本的 DataNode 继续读取,确保数据可靠性。
- NameNode只提供地址,不传数据。NameNode 的作用只是告诉客户端块存储在哪些 DataNode 上,它本身不参与任何数据传输。
- 拼接完整文件。最后,客户端会将从不同块读取到的数据按顺序拼接起来,组成一个完整的文件。
一个比喻:分布式图书馆借书
- 把 HDFS 比作一个超大图书馆,图书馆有很多分馆(DataNode),中央管理系统(NameNode)负责记录所有书的分布。假设我们要借一套书,整个流程如下:
- 询问中央管理系统:去问这套书在哪些分馆。
- 返回分馆列表:中央系统告诉你,这套书被拆成几部分,每部分在哪些分馆有。
- 挑选最近的分馆:根据距离和分馆状态,挑选优先级最高的分馆去拿书。
- 从分馆取书:每次从一个分馆取一部分,直到拿全所有部分。
- 书籍完整性检查:每次取完一部分都检查一下内容是否正确。
- 遇到问题换分馆:如果某个分馆的书有问题,就去其他存有副本的分馆拿书。
- 拼成完整的书:最后把书的各部分拼起来,完成借书。