今天学习了 Hadoop 的 HDFS 组件的架构设计和原理。
emmm… 多数是 Copy 的 官方文档。
HDSF
是一种分布式的文件系统,可以部署在通用的廉价机器,架构是 Master/Slave 模式的,具有 高容错、高吞吐量 的特点。官方上说:“ HDFS 在最开始是作为 Apache Nutch 搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。这个项目的地址是 https://hadoop.apache.org/core/ 。” 而实际上,应该是参考了 Google 公司的 GFS
分布式文件系统(被开源。。)。
HDFS 几个重要设计原则
硬件错误是常态而不是异常。这句话非常著名,意思就是说 HDFS 面向的是廉价的机器,任何一个组件都可能出现故障,因此 错误检测 和 快速自动恢复 是 HDSF 最核心的目标。
简单的一致性模式。HDFS 实现的是一个 “一次写入多次访问” 的文件访问模型,一个文件经过创建、写入 、关闭后就不再需要改变。这一假设简化了数据一致性问题,提高了 吞吐量。
“移动计算比移动数据更划算”
这句话也十分有名。一个应用请求的计算,离它操作的数据越近就越高效,在数据达到海量级别的时候更是如此。因为这样就能降低网络阻塞的影响,提高系统数据的吞吐量。将计算移动到数据附近,比之将数据移动到应用所在显然更好。HDFS为应用提供了将它们自己移动到数据附近的接口。
HDFS 架构
Master/Slave 架构
HDFS 使用的是 Master / Slave 模式,一个 HDFS 集群由一个 NameNode
和 多个 DataNode
组成。这是官网上面的架构图:
NameNode: 名称节点。中心服务器,负责管理 FS 的 命名空间(namespace)以及 客户端对文件的访问(控制流)。在 Hadoop 中, NameNode 一般是 单点故障 所在,可以配置多个 NameNode 的备份。(很一般的策略。)
DataNode: 数据节点。通常一个 节点(机器)一个 DataNode,负责管理它上面的数据的存储。每个 DataNode 中以 块(Block)为单位存储数据,一个 Block 的大小通常取为 64M。根据 配置的值,每个块可以在不同 DataNode 中具有多个备份。
以 replication 为例,一般同一个机架内 备份数为2,另外一个机架的 备份数为 1。这样可以提高系统的可靠性。
Rack: 机架。一个集群可有多个机架,主机之间,机架之间均通过 网络 互联,同一机架之间的主机的网络代价较小,而不同机架之间的主机网络代价更大。
- Client: HFDS 客户端。 Client 与 NameNode 之间 通过 ClientProtocol 协议交互 (控制流,发送指令等);与 DataNode 交换的是 文件数据(数据流); 而 DataNode 与 NameNode 通过 DatanodeProtocol 协议交互,发送 心跳包 和 块状态报告,并且接收指令。NameNode 永远不会由 数据流 经过。
数据的组织 与 备份
FS 的命名空间(namespace)
HDFS 支持传统的层次型文件组织结构(树形)。FS 的 命名空间和大多数现有的 FS 相似:用户可以创建、删除、移动 或 重命名 文件夹,但是不支持 磁盘配额 和 访问权限控制,也不支持 硬连接 和 软链接。
Namenode 负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被 Namenode 记录下来。应用程序可以设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由 Namenode 保存的。
数据副本
数据副本 是 HDFS 可靠性 和 性能的关键。这里指的数据副本的单位是 DataNode 上的数据块(Block)。
HDFS 采用一种称为 机架感知(rack-aware) 的策略来改进数据的可靠性、可用性和网络带宽的利用率。目前实现的副本存放策略只是在这个方向上的第一步。实现这个策略的短期目标是验证它在生产环境下的有效性,观察它的行为,为实现更先进的策略打下测试和研究的基础。。。
通过 rack-aware 过程,NameNode 可以知道每个 DataNode 的所属的机架id。一个简单未优化的策略是:将副本存放在不同的机架上,这样可以防止整个机架失效时的数据丢失,并且允许读取数据时利用多个机架的带宽。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效情况下的负载均衡。但是,因为这种策略的一个写操作需要传输数据块到多个机架,这增加了写的代价。
在大多数情况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架的节点上,一个副本放在同一机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,这就提高了写操作的效率。机架的错误远远比节点的错误少,所以这个策略不会影响到数据的可靠性和可用性。于此同时,因为数据块只放在两个(不是三个)不同的机架上,所以此策略减少了读取数据时需要的网络传输总带宽。在这种策略下,副本并不是均匀分布在不同的机架上。三分之一的副本在一个节点上,三分之二的副本在一个机架上,其他副本均匀分布在剩下的机架中,这一策略在不损害数据可靠性和读取性能的情况下改进了写的性能。
当前,这里介绍的默认副本存放策略正在开发的过程中。。。
副本选择
为了降低整体的带宽消耗和读取延时,HDFS 会尽量让读取程序读取离它最近的副本。如果在读取程序的同一个机架上有一个副本,那么就读取该副本。如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。
FS 元数据的持久化
FS 元数据 指的是 NameNode 上面的 命名空间。
任何对 FS 元数据的修改(增删改目录,文件,副本系数等),NameNode 都会将此用称为
EditLog
的事务日志记录。例如,在HDFS中创建一个文件,Namenode就会在Editlog中插入一条记录来表示;同样地,修改文件的副本系数也将往Editlog插入一条记录。Namenode在本地操作系统的文件系统中存储这个Editlog。整个 FS 的命名空间,包括数据块到文件的映射、文件的属性等,都存储在一个称为
FsImage
的文件中,这个文件也是放在 Namenode 所在的本地文件系统上。元数据磁盘错误
FsImage 和 Editlog 是 HDFS 的核心数据结构。如果这些文件损坏了,整个 HDFS 实例都将失效。因而,Namenode可以配置成支持维护多个 FsImage 和 Editlog 的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低Namenode每秒处理的名字空间事务数量。然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们也非元数据密集的。当Namenode重启的时候,它会选取最近的完整的FsImage和Editlog来使用。
Namenode是HDFS集群中的单点故障(single point of failure)所在。如果Namenode机器故障,是需要手工干预的。目前,自动重启或在另一台机器上做Namenode故障转移的功能还没实现。。。
HDFS 执行流程
NameNode 的启动
NameNode 将对 文件系统的改动 追加保存到本地文件系统上的一个日志文件(edits)。当一个 NameNode 启动时,它首先从一个映像文件(fsimage)中读取HDFS的状态,接着应用日志文件中的 edits 操作。然后它将新的HDFS状态写入(fsimage)中,并使用一个空的 edits 文件开始正常操作。
因为NameNode只有在启动阶段才合并fsimage和edits,所以久而久之日志文件可能会变得非常庞大,特别是对大型的集群。日志文件太大的另一个副作用是下一次NameNode启动会花很长时间。
Secondary NameNode 定期合并 fsimage 和 edits 日志,将 edits 日志文件大小控制在一个限度下。因为内存需求和NameNode 在一个数量级上,所以通常 secondary NameNode 和 NameNode 运行在不同的机器上
安全模式
Namenode 启动后会进入一个称为 安全模式 的特殊状态。处于安全模式的Namenode是不会进行数据块的复制的。Namenode 从所有的 Datanode 接收 心跳信号和 块状态报告。块状态报告包括了某个 Datanode 所有的数据块列表。每个数据块都有一个指定的最小副本数。当Namenode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的;在一定百分比(这个参数可配置)的数据块被Namenode检测确认是安全之后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他 Datanode 上 。
Staging
Client 创建文件的请求 其实并没有立即发送给 Namenode,事实上,在刚开始阶段 HDFS Client 会先将文件数据缓存到本地的一个临时文件tmp。应用程序的 写操作 被 透明地 重定向到这个临时文件。当这个临时文件累积的数据量超过一个Block 的大小,Client 才会联系 Namenode。Namenode 将文件名插入文件系统的层次结构中,并且分配一个数据块给它。然后返回 Datanode 的 标识符 和 目标数据块 给 Client。接着 Client 将这块数据从本地临时文件 tmp 上传到指定的 Datanode 上。当文件关闭时,在临时文件中剩余的没有上传的数据也会传输到指定的 Datanode 上。然后 Client 告诉 Namenode 文件已经关闭。此时 Namenode 才将 文件创建操作 提交到 EditLog 日志里进行存储。如果 Namenode 在文件关闭前宕机了,则该文件将丢失。
流水线复制
Datanode 能流水线式地从前一个节点接收数据,并在同时转发给下一个节点,数据以流水线的方式从前一个 Datanode 复制到下一个。
当 Client 向 HDFS 文件写入数据的时候,一开始是写到本地临时文件 tmp 中。假设该文件的副本系数设置为3,当本地临时文件累积到一个数据块的大小时,Client 会从 Namenode 获取一个 Datanode 列表用于存放副本。然后客户端开始向第一个 Datanode 传输数据,第一个 Datanode 一小部分一小部分(4 KB)地接收数据,将每一部分写入本地仓库,并同时传输该部分到列表中第二个 Datanode 节点。第二个 Datanode 也是这样,一小部分一小部分地接收数据,写入本地仓库,并同时传给第三个 Datanode 。最后,第三个 Datanode 接收数据并存储在本地。
存储空间的回收
文件的删除和恢复
当用户或应用程序删除某个文件时,这个文件并没有立刻从 HDFS 中删除。实际上,HDFS 会将这个文件 重命名 移动到 /trash 目录。只要文件还在 /trash 目录中,该文件就可以被迅速地恢复。
文件在 /trash 中保存的时间是可配置的,当超过这个时间时,Namenode就会将该文件从名字空间中删除。删除文件会使得该文件相关的数据块被释放。注意,从用户删除文件到HDFS空闲空间的增加之间会有一定时间的延迟。
减少副本系数
当一个文件的 副本系数 被减小后,Namenode 会选择过剩的副本删除。下次 心跳检测 时会将该信息传递 Datanode。Datanode 遂即移除相应的数据块,集群中的空闲空间加大。同样,在调用setReplication API结束和集群中空闲空间增加间会有一定的延迟。
健壮性保证
HDFS 的主要目标就是即使在出错的情况下也要保证数据存储的可靠性。常见的三种出错情况是:Namenode 出错, Datanode 出错 和 网络割裂(network partitions)。
磁盘数据错误,心跳检测和重新复制
每个 Datanode 节点周期性地向 Namenode 发送心跳信号。网络割裂可能导致一部分 Datanode 跟 Namenode 失去联系。Namenode 通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号 Datanode 标记为宕机,不会再将新的IO请求发给它们。任何存储在宕机 Datanode 上的数据将不再有效。Datanode 的宕机可能会引起一些数据块的副本系数低于指定值,Namenode 不断地检测这些需要复制的数据块,一旦发现就启动复制操作。
在下列情况下,可能需要重新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的副本系数增大。
数据校验
从某个 Datanode 获取的数据块有可能是损坏的,损坏可能是由 Datanode 的存储设备错误、网络错误 或者 软件bug 造成的。HDFS 客户端软件实现了对HDFS文件内容的 校验和(checksum) 检查。当 客户端创建一个新的HDFS文件,会计算这个文件每个数据块的校验和,并将校验和作为一个单独的 隐藏文件 保存在同一个 HDFS 名字空间下。当客户端获取文件内容后,它会检验从 Datanode 获取的数据跟相应的校验和文件中的校验和是否匹配,如果不匹配,客户端可以选择从其他Datanode获取该数据块的副本。
负载均衡
HDFS 的架构支持 数据均衡 策略。如果某个 Datanode 节点上的 空闲空间 低于特定的临界点,按照均衡策略系统就会自动地将数据从这个 Datanode 移动到其他空闲的 Datanode。当对某个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并且同时重新平衡集群中的其他数据。这些均衡策略目前还没有实现。。。