# HDFS 的架构设计及组件
# HDFS 的特点
- 高容错: 由于 HDFS 采用数据的多副本方案,所以部分硬件的损坏不会导致全部数据的丢失。
- 高吞吐量: HDFS 设计的重点是支持高吞吐量的数据访问,而不是低延迟的数据访问。
- 大文件支持: HDFS 适合于大文件的存储,文档的大小应该是是 GB 到 TB 级别的。
- 简单一致性模型: HDFS 更适合于一次写入多次读取 (write-once-read-many) 的访问模型。支持将内容追加到文件末尾,但不支持数据的随机访问,不能从文件任意位置新增数据。
- 跨平台移植性: HDFS 具有良好的跨平台移植性,这使得其他大数据计算框架都将其作为数据持久化存储的首选方案。
# HDFS 的架构设计
# 1. 架构的设计原理
HDFS 采用 master/slave 架构。一个 HDFS 集群是由一个 Namenode 和一定数目的 Datanodes 组成。Namenode 是一个中心服务器,负责管理文件系统的名字空间 (namespace) 以及客户端对文件的访问。集群中的 Datanode 一般是一个节点一个,负责管理它所在节点上的存储。HDFS暴露了文件系统的名字空间,用户能够以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组 Datanode 上。 Namenode 执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体 Datanode 节点的映射。Datanode 负责处理文件系统客户端的读写请求。在 Namenode 的统一调度下进行数据块的创建、删除和复制。

# 2. 架构的稳定性
# 1. 心跳机制和重新复制
每个 DataNode 定期向 NameNode 发送心跳消息,如果超过指定时间没有收到心跳消息,则将 DataNode 标记为死亡。NameNode 不会将任何新的 IO 请求转发给标记为死亡的 DataNode,也不会再使用这些 DataNode 上的数据。 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode 会跟踪这些块,并在必要的时候进行重新复制。
# 2. 数据的完整性
由于存储设备故障等原因,存储在 DataNode 上的数据块也会发生损坏。为了避免读取到已经损坏的数据而导致错误,HDFS 提供了数据完整性校验机制来保证数据的完整性,具体操作如下:
当客户端创建 HDFS 文件时,它会计算文件的每个块的 校验和,并将 校验和 存储在同一 HDFS 命名空间下的单独的隐藏文件中。当客户端检索文件内容时,它会验证从每个 DataNode 接收的数据是否与存储在关联校验和文件中的 校验和 匹配。如果匹配失败,则证明数据已经损坏,此时客户端会选择从其他 DataNode 获取该块的其他可用副本。
# 3.元数据的磁盘故障
FsImage 和 EditLog 是 HDFS 的核心数据,这些数据的意外丢失可能会导致整个 HDFS 服务不可用。为了避免这个问题,可以配置 NameNode 使其支持 FsImage 和 EditLog 多副本同步,这样 FsImage 或 EditLog 的任何改变都会引起每个副本 FsImage 和 EditLog 的同步更新。
# 4.支持快照
快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。
# HDFS 的组件
HDFS 包含 NameNode、DataNode、SecondaryNameNode 三个组件。
# 1. NameNode(名称节点)
用来管理文件系统的命名空间,负责记录文件是如何分割成数据块,以及这些数据块分别被存储到哪些数据节点上。
- 维护 HDFS 文件系统,是 HDFS 的主节点。
- 接受客户端(命令行、Java程序)的请求: 创建目录、上传数据、下载数据、删除数据等。
- 管理和维护HDFS的日志和元信息。
第一次启动 NameNode 格式化后,创建 edits 和 fsimage 文件。如果不是第一次启动,直接加载 edits 日志和 fsimage 文件到内存。
日志文件(edits文件):记录的是客户端的所有操作,存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到edits文件中,同时体现了HDFS的最新的状态。
(*)而日志 edits文件 保存在我们配置的数据块目录中:由 hadoop.tmp.dir 参数指定。路径:$hadoop.tmp.dir/dfs/name/current

(*)使用 hdfs oev -i 命令查看 edit日志输出为 XML 文件:
# 举个栗子 hdfs dfs -mkdir /edit_log hdfs oev -i edits_inprogress_0000000000000000070 -o /tmp/log.xml
元信息文件(fsimage文件):记录的是数据块的位置信息、数据块的冗余信息。
(*)保存目录: $hadoop.tmp.dir/dfs/name/current

(*)使用 hdfs oiv -i 命令将 fsimges 日志输出为 XML
# 举个栗子 hdfs oiv -i fsimage_0000000000000000069 -o ~/tmp/fsimage.xml -p XML
每次 NameNod 启动的时候都会将 fsimage 文件读入内存,加载 edits 里面的所有操作,保证内存中的元数据信息是最新的、同步的,可以看成 NameNode 启动的时候就将 fsimage 和 edits 文件进行了合并。
# 2. DataNode(数据节点)
文件系统的数据节点,根据需要存储和检索数据块,并定期向 NameNode 发送他们所存储的块列表。
以数据块为单位,保存数据。
- Hadoop 1.x 的数据块大小: 64M。
- Hadoop 2.x 的数据块大小:128M。
- Hadoop 3.x 的数据块大小:128M。
数据保存的目录:由 hadoop.tmp.dir 参数指定。如:
/usr/software/hadoop-2.10.0/tmp/dfs/data/current/BP-1940172841-192.168.3.11-1588315718387/current/finalized/subdir0/subdir0已文件的文件的形式进行保存(blk_***)

在全分布模式下,至少两个 DataNode 节点。
# 3. SecondaryNameNode(第二名称节点)
与 NameNode 进行通讯,定期保存 HDFS 元数据的快照,用以备份和恢复数据等操作,其中一个主要作用是进行日志的合并。
- SecondaryNameNode 询问 NameNode 是否需要 checkpoint。直接带回 NameNode 是否检查结果。
- SecondaryNameNode 请求执行 checkpoint。
- NameNode 滚动正在写的 edits 日志。
- 将滚动前的编辑日志和镜像文件拷贝到 SecondaryNameNode。
- SecondaryNameNode 加载编辑日志和镜像文件到内存,并合并。
- 生成新的镜像文件 fsimage.chkpoint。
- 拷贝 fsimage.chkpoint 到 NameNode。
- NameNode 将 fsimage.chkpoint 重新命名成 fsimage。
# 4. Checkpoint 时间检查点设置
- 通常情况下,SecondaryNameNode每隔一小时执行一次。对应的配置文件:hdfs-default.xml
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600</value>
</property>
- 一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次。
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60</value>
<description> 1分钟检查一次操作次数</description>
</property>
# 5. 总结
在 NameNode节点上,fsimage 保存了元数据镜像文件,而 edits中完整记录了元数据的操作日志(针对文件系统做得修改操作记录)。NameNode 内存中存储的元数据可以用“fsimage+edits”来表达。而 SecondaryNameNode 负责定时(默认1小时 或 数据达到配置量时)从 NameNode 上获取 fsimage 和 edits进行合并,然后在发送给 NameNode,减少 NameNode 的工作量。