在上一篇《大数据(2):Hadoop 启动进程》中详细分析了 Hadoop 进程的启动方法,这一篇将分析 Hadoop 中一个重要模块 HDFS。
一、什么是 HDFS
HDFS(Hadoop Distributed File System)是 Hadoop 项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于商用服务器上。它所具有的高容错、高可靠性、高可扩展性、高获得性、高吞吐率等特征为海量数据提供了不怕故障的存储,为超大数据集(Large Data Set)的应用处理带来了很多便利。
简单来说:
首先,它是一个文件系统,用于存储文件,通过统一的命名空间——目录树来定位文件;
其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
二、HDFS 架构
整个 HDFS 集群由 NameNode 和 DataNode 构成主/从模式。一个 HDFS 集群在同一时间,只能有一个激活的 NameNode 名称节点,这是一个主服务器,它管理文件系统名称空间,并控制客户端对文件的访问,但从 Hadoop 3 开始,可以有多个待命态的 NameNode。
此外,还有多个 DataNodes 数据节点。这些 DataNodes 管理节点的存储。HDFS 公开一个文件系统名称空间,并允许用户数据存储在文件中。在内部,文件被分成一个或多个块,这些块存储在一组数据节点中。
三、NameNode
NameNode 主要负责两件事情:维护文件系统目录和 DataNode 状态监控。
1. 维护文件系统目录
NameNode 负责整个分布式文件系统的元数据,包括文件目录树,文件到数据块 Block 的映射关系等。这些数据保存在内存里(这要求 NameNode 内存须足够大),同时这些数据还以两个文件形式永久保存在本地磁盘上:命名空间镜像文件 (fsimage) 和编辑日志文件 (edits)。
fsimage 文件存储的是文件系统元数据信息,如文件及目录结构,组成文件的块的信息,副本数量信息等;edits 文件则记录着用户对文件的各种操作记录,对 hdfs 中的文件进行新增或者修改操作,操作记录首先被记入 edits 文件中,当客户端操作成功后,相应的元数据会更新到内存 meta.data 中,防止发生意外导致丢失内存中的数据。fsimage 和 edits 两个文件结合可以构造出完整的内存数据。
注:在高可用模式下,edits 文件将不会存储在 NameNode 中,而是存储在 JournalNodes (JNs) 中。
当 Active NameNode 的名称空间修改时,会把修改内容记录到 JNs 中。Standby NameNode 能够从 JNs 读取修改内容,不断监听,并将这些修改,应用到自己的名称空间,以确保在发生故障之前完全同步名称空间状态。
2. 状态监控
NameNdoe 还负责对 DataNode 的状态监控。DataNode 通过心跳机制,定期向 NameNode 发送存储块的列表信息。NameNode 可以知道每个 DataNode 上保存着哪些数据(Block信息),DataNode 是否存活,并可以控制 DataNode 启动或停止。
若 NameNode 发现某个 DataNode 发生故障,会将其存储的数据在其他 DataNode 机器上增加相应的备份以维护数据副本(默认为3)保持不变。
如果把整个 HDFS 比作一本书,则 NameNode 相当于书的目录,记录着每一节的标题以及对应的页码;DataNode 相当于书的详细内容。由于书太厚,当想找书中的内容时,必须通过目录来查找。此外,若书的内容有修改,目录也需要做出调整,确保目录与内容一致。
如果 NaneNode 服务器失效,则整个集群将不可访问。因为我们无法知道哪些 DataNode 存储着哪些数据。但若部分 DataNode 节点失效,集群影响不大,任可正常对外提供服务。因此 NameNode 的高可用,容错机制很重要。
四、DataNode
DataNode 负责数据块的实际存储和读/写工作。
Hadoop 中,所有数据都被切分为一个或多个块 (block),每个块的默认大小为 128M。
当上传一个大文件时,HDFS 会将其切割成固定大小的 block,每个块以多份的形式存储在集群上,默认为 3 份(备份)。当需要查找这个文件时,并不是直接从 DataNode 读取这些 block 的信息,因为集群可能非常庞大,而这些 block 可能散落在不同的节点,因此需要先联系 NameNode,从 NameNode 中得知文件存储在哪些 DataNode 中,再从这些 DataNode 中读取数据。
这里引出两个问题:
为什么一个文件要分成多个块 (block) 存储 ?
如果不分块存储,当我们需要读取一个文件时,只能单线程从头开始读取文件,而当分为多个块时,就可以并行读区文件。
例如,上传一个 300M 的文件时,HDFS 会将文件切割成 block0 + block1 + block2,每块大小为 128M + 128M + 44M,3 份 block 同时开始写入;当读取时,也将从 3 个 block 中同时读取,读取后再将 3 份 block 拼接成一个文件。
假设磁盘读取速度为 100M/s,则理论上,HDFS 读取 300M 文件需要 1.28s,而普通文件系统从头至尾读取,需要 3s。文件越大,HDFS 的优势越明显。
HDFS 块大小为何是 128MB ?
这里 128M 指的是块的最大大小!每个块最多存储 128M 的数据,如果当前块存储的数据不满 128M,存了多少数据,就占用多少的磁盘空间,一个块只属于一个文件。
默认为128M的原因,基于最佳传输损耗理论!
不论对磁盘的文件进行读还是写,都需要先进行寻址!
最佳传输损耗理论:在一次传输中,寻址时间占用总传输时间的 1% 时,本次传输的损耗最小,为最佳性价比传输!
普通磁盘写的速率大概为100M/S,寻址时间一般为10ms,为使寻址时间为总传输时间的 1%,则每次总传输时间为 10ms / 1% = 1s,所以每次传输大小为 1s * 100M/S = 100M。
块在传输时,每 64K 还需要校验一次,因此块大小,必须为 2 的 n 次方,最接近 100M 的就是 128M!
当然,如果使用的硬盘读写的速度是 300M/S,应将块大小调整到 256M,如果使用的硬盘读写的速度是 500M/S,将块大小调整到 512M。
DataNode 进程死亡或者网络故障造成 DataNode 无法与 NameNode 通信,NameNode 不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。
如果一台 DataNode 经过 10 分 30 秒(默认)后没有给 NameNode 发送心跳信息,而被 NameNode 判断为死亡,NameNode 会马上将其上的数据备份到集群中其他机器上。
当这个 DataNode 节点排除故障后,重新回到集群中,该节点上还保存着原来那批数据,而默认的配置情况下,DataNode 会每隔 60 分钟向 NameNode 发送一次 block 信息,在这段时间内,集群中会有某些数据块多出一个备份。在 NameNode 收到该节点的 block 信息后,它发现数据备份多了才会命令某些 DataNode 删除掉多余的备份数据。
五、上传文件到 HDFS
上传一个文件到 HDFS 集群分以下几步。
客户端创建文件;
向 namenode 请求上传文件,namenode 响应客户端是否可以上传文件,询问第一块 block 该上传到哪里,namenode 返回 3 个 datanode 地址给客户端;
客户端请求与最近的一个 datanode 节点 (假设为 datanode1) 建立传输通道 (data queue),并告知其还要传给 datanode2 和 datanode3。datanode1 会请求与 datanode2 建立连接 (pipeline),datanode2 请求与 datanode3 建立连接;
客户端收到通道建立成功的消息后,开始向 datanode1 发送 block1 的数据,以一个个 package (64k) 为单位通过通道向 datanode1 写数据,datanode1 收到数据会将其存在本地缓存中,一边向 datanode2 传数据,一边将缓存中的数据保存到磁盘上;
客户端在传送数据时会有一个 package 的应答队列,datanode1 每收到一个 package 后就向客户端发回消息 (ack),datanode1 不用等待 datandoe2 发回应答信息才给客户端发送信息,客户端只保证 datanode1 收到了数据就行,后面的事它交给了 datanode1,当一个 block 传输完成之后,客户端再次请求 namenode 上传第二个 block。
六、从 HDFS 下载文件
从 HDFS 集群中下载文件时,主要分以下几步。
跟 Namenode 通信,请求下载某个数据;
Namenode 查询元数据信息以及 block 位置信息,将数据所在的 Datanode 信息返回给客户端;
客户端根据数据所在的 Datanode,挑选一台距离自己最近的 Datanode,并向其发出下载文件的请求(若所需数据不在一台 Datanode 上保存,则分别向多台 Datanode 发出请求);
Datanode 响应客户端请求,将数据返回给客户端;
从多个 Datanode 获得的数据不断在客户端追加,形成完整的数据。
七、HDFS 的缺点
HDFS 不适合以下操作:
低延迟数据访问:比如毫秒级、低延迟与高吞吐率。
小文件存取:小文件将占用 NameNode 大量内存,导致寻道时间超过读取时间。
并发写入、文件随机修改:一个文件只能有一个写者,仅支持 append。
HDFS文件系统为什么不适用于存储小文件?
这与 HDFS 系统底层设计实现有关系的,HDFS 本身的设计就是用来解决海量大文件数据的存储,天生喜欢大数据的处理,大文件存储在 HDFS 中,会被切分成很多的小数据块,也就是说任何一个文件不管有多小,都是一个独立的数据块,而这些数据块的信息则是保存在元数据中的,namenode中会存储元数据的信息。
例如,有 100 个 1M 的文件存储进入 HDFS 系统,那么数据块的个数就是 100 个,元数据的大小就是 100*150byte,消耗了15000byte 的内存,但是只存储了 100M 的数据。
有 1 个 100M 的文件存储进入 HDFS 系统,那么数据块的个数就是 1 个,元数据的大小就是 150byte,消耗量 150byte 的内存,存储量 100M 的数据。
也就是说,太多的小文件,将导致目录比内容还多,所以说 HDFS 文件系统不适用于存储小文件。
总结
这一篇主要了解 HDFS 的两个关键节点:NameNode 和 DataNode,并分析了 HDFS 上传、下载文件的流程,解决了数据的分布式存储。
下一篇《大数据(4):MapReduce》讲解 Hadoop 的并行计算框架。