[翻译] HDFS架构

2023年 7月 14日 127.0k 0

原文链接, 版本: 3.3.6

没有对照原文逐字翻译, 有微调

简介

HDFS(Hadoop Distributed File System)是一个分布式文件系统, 期望可以运行在廉价机器上。它与现有的分布式系统有很多相似之处, 然而,在某些设计上有着显著差异。HDFS具有高度容错性,并且被设计运行在廉价机器上。HDFS支持对应用数据的高吞吐量访问,适用于拥有海量数据的应用。HDFS放宽了一些POSIX协议的要求, 以实现对文件系统数据的流式访问。 HDFS原本是作为Apache Nutch 项目的基础设施而开发的,目前已经成为Apache Hadoop项目的核心组成部分。

假定现实与设计目标

硬件故障

首先硬件故障无法避免。一个HDFS集群可能由成百上千台服务器组成,每台服务器存储了整个文件系统的部分数据, 这就意味着HDFS在运行时,总有一些服务器可能会遭遇故障。因此,故障的检测和自动恢复能力是HDFS的核心架构目标之一。

流式数据访问

基于HDFS的应用需要对其数据集进行流式访问。HDFS在设计上优先考虑的是批量处理而不是用户交互使用。重点是提高数据访问吞吐量而非降低数据访问延迟。对于那些基于HDFS来进行文件读取存储的应用而言, 很多POSIX协议的特性都没有用到,也就不需要满足POSIX的一些强制性要求, 因此, 一些关键领域的POSIX语义已经被调整以提高数据吞吐量。

大型数据集

基于HDFS的应用通常需要与海量数据进行交互。在HDFS中,一个文件通常是 GB 到 TB大小,因此,HDFS经过调优后,可以支持大文件了。并且它应该提供高聚合数据带宽,并能在单个实例中扩展到数百个节点、单实例支持千万级别文件数量。

简单一致性模型

HDFS应用程序需要支持一次写入多次读取的文件访问模型。一个文件一旦创建、写入然后关闭, 除了append和truncate操作, 其它就不需要再对其内容进行修改了。HDFS仅支持在文件末尾追加内容,不能在任意位置更新内容。这个假设简化了数据一致性问题,从而保证了高吞吐量的数据访问。例如MapReduce程序或者网络爬虫程序就很适合这种模型。

迁移算力比迁移数据更划算

如果计算所需要的数据刚好在计算节点附近,那么计算效率将会明显提高,当数据集很大时,这将尤其明显。这样不仅可以减少网络拥塞,还能提高系统整体的吞吐量。所以假定: 将计算迁移到数据所在的位置比把数据迁移到计算的位置更好。HDFS为应用程序提供了接口,方便它们迁移自身的算力到数据所在位置的附近。

可移植性

HDFS在设计之初就考虑到了不同平台之间的可移植性, 这有助于HDFS被更广泛地接受。

NameNode和DataNodes

HDFS是基于master/slave模式的架构。一个HDFS集群包含一个单独的NameNode节点, 它充当master角色, 主要管理文件系统的命名空间和客户端的常规文件访问。另外, 集群里还有若干个DataNode, 通常是每个节点一个,负责管理它所运行节点的数据存储。HDFS提供了一个文件系统命名空间并且允许用户将数据存储在文件中。在内部, 一个文件会被分割成一块或多块,并且这些数据块会被存到一组DataNode上面。NameNode负责执行文件系统命名空间操作, 像打开、关闭和重命名文件或目录, 它还确定了块和DataNodes的映射关系。DataNode则为客户端提供读写服务,同时还执行来自NameNode的指令, 例如创建、删除和复制数据块。

NameNode和DataNode是被设计用于在普通计算机上运行的组件。这些计算器通常运行GUN/Linux操作系统。HDFS是用Java语言构建的,任何机器只要支持Java,就能运行NameNode和DataNode。基于拥有高度可移植性Java语言来开发,意味着HDFS可以被部署到各种不同类型的计算机上。一个典型的部署方式是这样的: NameNode单独部署一台, 集群里的其他机器都部署一个DataNode。 虽然架构上允许在一台服务器上运行多个DataNode节点, 但实际部署一般不会这么干。集群中只保留一个NameNode极大简化了系统架构, NameNode负责记录和管理HDFS集群上的元数据信息,并且负责决策。 在这种设计下,用户的数据不会流经NameNode。

文件系统命名空间

HDFS支持传统的文件组织结构, 用户或应用可以创建文件夹然后在文件夹内部存储文件。HDFS的文件系统命名空间层次结构与其它大多数文件系统类似,支持创建、删除文件、移动文件和重命名文件。HDFS支持用户配额和访问权限控制但不支持硬连接或软连接, 但未来可能会去实现。虽然HDFS遵循FileSystem的命名规范, 但某些路径和名称(例如 /.reserved 和 .snapshot) 是保留的。像传输加密和快照功能会使用这些保留路径。

数据复制

HDFS被设计用于在一个集群的多台机器上可靠地存储超大文件。每个文件都会被存储为一系列的块, 为了实现容错性,数据块会被保留多份。每个文件的的块大小和复制因子都可以单独设置。

除了最后一个块,一个文件的其它块的大小应该都是一样的, append和hsync操作在块支持可变长度后,可以在当前块没有填满时,开启新的块。

应用可以在文件创建的时候指定副本数量,也可以在后期进行调整。HDFS中的文件是一次写入的(除了append和truncate操作),并且任何时候只能有一个写入者。

NameNode的决策会影响数据块的所有副本,它周期性接收来自DataNode的心跳和状态报告。接收到心跳表示DataNode正常运行, 状态报告包含了DataNode机器上所有数据块的信息。

副本放置: 初步尝试中

副本的放置对于HDFS的可靠性和性能提升至关重要。副本放置策略也是HDFS与大多数其他分布式文件系统的区别之一。这是一个需要大量调试和经验判断的特性。机架感知副本放置策略旨在提高数据可靠性、可用性以及网络带宽利用率。当前实现的副本放置策略也是在这个方向进行的初步尝试。短期目标是在生产系统中验证它,了解更多的关于其行为的信息,并以此为基础,测试和研究更复杂的策略。

大型HDFS集群通常运行在分布于多个机架上的多台服务器上。不同机架上的两个节点的通信必须要经过交换机。在大多数情况下, 相同机架上服务器之间的带宽要比不同机架上的带宽要高。

通过Hadoop机架感知, NameNode确定了每个DataNode所属的机架id。一种简单但非最优的策略就是把副本放到不同的机架的服务器上,这样做有两个好处,一个是防止整个机架上的服务器宕机导致数据丢失,另一个是当读取数据时,可以很好的利用不同机架的带宽。这种策略可以在集群中均匀地放置副本, 这样在组件发生故障时,也可以很好的做负载均衡。然而这种策略会增加写输入的消耗,因为需要写入到不同的机架。

一般情况, 当副本数为3时, HDFS的副本放置策略是如果写入的服务器上刚好也有一个DataNode, 则会直接在这个DataNode上设置一个副本。如果本机没有DataNode, 则会在写入者所属的机架上随机选个DataNode, 第二个副本会选择另外一个随机的机架,第三个副本会在第二个副本所属机架上选择一个其他服务器。这种策略减少了机架之间的写入流量,因此可以提高写入性能。机架故障的可能性要远远小于发生节点故障,所以这个策略不会影响数据的可靠性和可用性。然而,因为数据块存放的位置从3个机架变成了两个机架,当读取数据时,并不能减少聚合网络带宽的使用。总的来说, 一个数据块的副本不会均匀分布在各个机架上,两个副本会存放到同一个机架的不同节点,另一个副本存放于另一个机架的一个节点上,这个策略在不影响数据可靠性和读性能的前提下,提高了写性能。

当副本数超过3时,第4个及之后的副本存放位置是随机确定的, 但是会满足任意一个机架上面的副本数不会超过一个上限,上限 = (副本数 - 1)/(机架数 + 2)。

因为NameNode不允许一个DataNode拥有多个一样的副本,所以副本数最多只能是当前DataNode的数量。(举个例子: 如果DataNode数量是2, 副本数设置为3, 实际产生副本数是2, 因为第三个副本无论存放到哪里都会导致同一个DataNode保存了多个一样的副本)

在HDFS添加了对存储类型和存储策略的支持后, NameNode在副本放置时除了会考虑上述的机架感知外,还会考虑存储策略。具体来说,NameNode会先基于机架感知策略选择存储节点,然后检查候选节点是否具备关联文件的存储要求。如果候选节点不具备所需的存储类型, NameNode就会重新寻找另一个节点。如果在第一次尝试中找不到足够的节点存放副本,NameNode会在第二次尝试中寻找具备备用存储类型的节点。

上面描述的默认副本放置策略, 正在开发中。

副本选择

为了最小化全局带宽消耗和读延迟, HDFS优先从距离读取者最近的副本进行数据读取。如果刚好同一机架就有目标副本, 那么会优先读这个副本。如果HDFS集群横跨多个数据中心,那么会优先读取本地数据中心的副本。

块放置策略

上面提到,如果副本数是3时,HDFS的策略是如果数据写入者恰好也在一个DataNode服务器上,则会在当前DataNode上放置一个副本,否则会在当前机架上随机选择一个DataNode存放副本,第二个副本会在另一个机架上随机选择一个节点,第三个副本会在第二个副本所属机架上再随机选择一个节点。如果副本数超过3, 从4个及之后的副本放置是随机确定的, 但是会满足任意一个机架上面的副本数不会超过一个上限,上限 = (副本数 - 1)/(机架数 + 2)。除此之外, HDFS还支持4种不同的可插拔块放置策略。用户可以根据需要,选择适合的策略。默认情况下, HDFS支持 BlockPlacementPolicyDefault 策略。

安全模式

当NameNode启动时,会进入一个特别的状态,称作安全模式. 当NameNode处于安全模式时, 数据块不能进行复制操作。NameNode会接收来自DataNode的心跳和状态报告信息。状态报告信息包含了该DataNode里面存放的所有块信息。每个数据块都确定了最小副本数,只有当数据块的最小副本数被NameNode确认了,才能被认为是已经安全复制。当安全复制块的占比达到了配置要求(除此之外,还要等个30秒),NameNode会退出安全模式。然后NameNode会找到副本数仍然少于指定数量的数据块列表, 并将这些数据块复制到其他DataNode上。

文件系统元数据的存储

NameNode存储了HDFS的命名空间, NameNode使用称为EditLog的事务日志保存了对文件系统元数据的每一次修改。举个例子,当创建一个新文件时,NameNode就会往EditLog里面记录一条关联日志。类似的,更改某个文件的副本数也会产生一条编辑日志。NameNode使用本机的一个文件来存储EditLog。整个文件系统命名空间,包括文件和数据块的映射关系和文件系统的属性,都会被存到一个文件里面,称为FsImage. FsImage和Editlog类似,也是存放在NameNode宿主机的一个本地文件上。

NameNode在内存中保留了整个文件系统的命名空间和文件块映射信息。当NameNode启动或者因为配置的阈值被触发时, 它会从磁盘读取FsImage和Editlog文件, 并且将EditLog文件里面的事务日志重做到内存中的Fsimage里面,并将此版本的FsImage刷新到磁盘。因为EditLog里面的事务已经被合并到了FsImage里面了,所以EditLog文件就会被截断, 这个过程被称为检查点(checkpoint)。检查点的目的是通过对文件系统进行快照并保存到FsImage中,以此来确保HDFS具有一致的文件系统元数据视图。尽管读取FsImage的效率很高,但是对FsImage直接进行增量编辑的效率并不高。我们不会为每个编辑操作来修改FsImage文件,而是将这些操作保存到EditLog里面。在检查点的时候,Editlog会被合并到FsImage里面。可以根据一定的时间间隔秒数(dfs.namenode.checkpoint.period)或者积累的事务数量(dfs.namenode.checkpoint.txns)来触发检查点。如果两个都设置了, 任意条件到达阈值都会触发检查点。

DataNode在本地文件系统保存数据, 并且并不需要知道存储的是什么,它将HDFS系统中的数据块存到本地单独的文件中。DataNode不会将全部文件创建到一个文件夹下面。相反, 它会根据算法来确定每个目录中的最佳文件数量,并相应地创建子目录。因为本地文件系统可能无法很好地支持在一个文件夹下面创建海量文件,所以HDFS也不这么干。当DataNode启动时,它会扫描它管理的本地文件,生成HDFS数据块信息列表,并且将这个报告信息发送给NameNode, 这个报告就称为 块报告。

通信协议

所有HDFS的通信协议都是基于TCP/IP协议之上。客户端与NameNode上配置好的端口建立TCP链接,它使用Client协议与NameNode通信。DataNode与NameNode之间的通信采用了DataNode协议。远程过程调用抽象封装了Client协议和DataNode协议。在设计上, NameNode不会主动发起任何RPC协议,它只会被动响应客户端或DataNode的请求。

健壮性

HDFS的首要目标是即使遭遇故障,也能可靠地存储数据。三种常见的故障包括: NameNode故障, DataNode故障和网络分区。

磁盘故障、心跳和重新复制

每个DataNode都会周期性地给NameNode发送心跳包。网络分区故障可能导致部分DataNode失去与NameNode的连接,NameNode通过检测心跳包的缺失来找到这些DataNode,同时会将这些最近没有心跳请求的DataNode标记为死亡状态并且不会将新产生的IO请求转发到这些DataNode上面。保存在死亡状态DataNode上面的数据也就无法再被访问。DataNode死亡会导致部分数据块的副本数少于设定的数量。NameNode会持续跟踪这些数据块,并在必要的时候进行复制操作。产生重新复制的原因有好几种: 某个DataNode失效了; 某个副本损坏了; DataNode的一块磁盘出现故障了或者文件副本数调整增加了。

默认DataNode离线超过10分钟才会被标记为死亡,设计这么长是为了防止因为DataNode状态波动导致产生复制风暴。用户可以设置更短的间隔以减少失效节点对性能要求较高的工作负载产生影响的可能。

集群重平衡

HDFS架构支持数据重平衡。如果某个DataNode的可用空间低于某个阈值,自平衡方案会自动将数据从一个DataNode转移到另一个DataNode。如果某个特定文件突然出现了高读取流量时, 重平衡方案可以动态创建额外的副本, 并在集群中重新平衡其他副本。这些类型的数据再平衡方案尚未实现。

数据完整性

从DataNode读取的数据块有可能在传输过程中损坏。多种原因可能会导致这种损坏,例如硬盘故障、网络错误或者软件问题。HDFS客户端实现了针对HDFS文件内容的校验和检查。当一个客户端创建了一个HDFS文件时, 它会为文件的每个数据块都计算一个校验和,并且会将这些校验和存储在同一HDFS命名空间的一个隐藏文件中。当客户端检索文件内容时, 它会验证它接收到的数据是否与关联的校验和一致。如果不一致,客户端可以选择其它拥有该数据块副本DataNode重新获取。

元数据磁盘故障

FsImage和EditLog文件是HDFS的核心数据结构。这两个文件的损坏可能直接导致HDFS实例的不可用。因此,NameNode支持同时维护多份FsImage和EditLog。每次更新都会同步更新到所有的FsImage和EditLog里面。同步更新多个FsImage和EditLog会降低NameNode事务处理速度,然而,这种损耗是可以接受的,尽管HDFS应用是数据敏感的,但是像元数据的修改不会产生太大损耗。当NameNode重启后,它会选择一份最新并且一致的FsImage和EditLog文件来使用。

另一种方案是搭建NameNode高可用,可以使用 基于NFS的共享存储方案或者分布式EditLog方案(Journal), 更推荐后面这个方案。

快照

快照支持在特定的时间点保存一份数据的副本。在HDFS中,快照的一个可能的用途是将一个受损的HDFS实例回滚到先前已知的良好的时间点。

数据组织

数据块

HDFS被设计用于支撑超大文件的处理。基于HDSF的应用通常也是要处理大数据集。文件一般是一次性写入然后需要被多次读取,并且要求达到流式读取的速度。HDFS支持文件的一次写入多次读取语义。HDFS中,典型的块大小是128MB, 因此一个HDFS文件会被分成128MB大小的块,HDFS也会尽可能将同一个文件的不同块放到不同的DataNode上。

复制流水线

当客户端往集群提交了一个副本数为3的文件, NameNode通过副本选择算法选中了一些DataNode,构建一个列表。列表里的每个DataNode都将保留一份数据块的副本。客户端首先向第一个DataNode写入。第一个DataNode开始逐段接收数据,然后将每段数据都写到本地存储库并且将这段数据传输到列表的第二个DataNode。第二个DataNode也是如此, 写到本地后,传输到第三个DataNode, 最终就有三个DataNode保留了该数据块的副本。因此, 一个DataNode可以在接收到客户端数据的同时,将数据转发到第二个DataNode,以此类推。

可访问性

应用程序可以通过多种方式访问HDFS.HDFS原生提供一个供应用使用的 Java API,此外还提供了一个C语言包装器封装了Java API, REST API也是可以用的。另外,还可以使用浏览器来访问存储于HDFS实例的文件。通过使用NFS网关, 可以将HDFS挂载为客户端本地文件系统的一部分。

FS Shell

HDFS允许用户将数据以文件或目录的形式进行组织。提供了一个名为FS shell的命令行接口, 让用户可以与存储于HDFS的数据进行交互。它的语法和人们熟知的shell命令语法类似, 下面展示一些例子:

目的 命令
创建 /foodir 文件夹 bin/hadoop dfs -mkdir /foodir
删除 /foodir 文件夹 bin/hadoop fs -rm -R /foodir
查看 /foodir/myfile.txt 内容 bin/hadoop dfs -cat /foodir/myfile.txt

FS shell是为那些只能以脚本语言来和存储数据交互的应用准备的。

DFSAdmin

DFSAdmin命令集用于管理HDFS集群。这些命令应该仅由管理员使用。下面展示一些例子:

目的 命令
进入安全模式 bin/hdfs dfsadmin -safemode enter
获取 DataNode 列表 bin/hdfs dfsadmin -report
刷新 DataNode 信息 bin/hdfs dfsadmin -refreshNodes

浏览器接口

典型的HDFS安装会配置一个web服务端。允许用户查看存储于HDFS的文件。

空间回收

文件删除与恢复

如果启用了回收站配置, 通过FS Shell删除的文件不会被立即删除,而是被HDFS移动到了trash目录下(默认在 /user//.Trash 这个目录下)。这些文件可以被快速恢复只要它还在回收站里。

大部分最近删除的文件都会被已到回收站 (/user//.Trash/Current), 并且在一个可配置的间隔后,HDFS会为当前回收站目录中的文件创建检查点,并且在过期时,删除旧的检查点。有关回收站的检查点相关信息, 请参考 FS Shell中的expunge命令。

当处于回收站的文件也过期了,NameNode将会从HDFS里面彻底删除这些文件。删除也会释放这个文件关联的数据块空间。注意, 从用户删除文件到实际磁盘空间释放会有个很明显的时间差。

下面展示通过FS Shell进行文件删除的操作示例。我们在delete文件下创建了两个文件test1和test2

$ hadoop fs -mkdir -p delete/test1
$ hadoop fs -mkdir -p delete/test2
$ hadoop fs -ls delete/
Found 2 items
drwxr-xr-x   - hadoop hadoop          0 2015-05-08 12:39 delete/test1
drwxr-xr-x   - hadoop hadoop          0 2015-05-08 12:40 delete/test2

删除test1, 提示写的很明显,文件被转移到了回收站。

$ hadoop fs -rm -r delete/test1
Moved: hdfs://localhost:8020/user/hadoop/delete/test1 to trash at: hdfs://localhost:8020/user/hadoop/.Trash/Current

然后我们加上 -skipTrash 选项来删除test2, test2将会立即被彻底删除

$ hadoop fs -rm -r -skipTrash delete/test2
Deleted delete/test2

我们在回收站也只能看到test1

$ hadoop fs -ls .Trash/Current/user/hadoop/delete/
Found 1 items
drwxr-xr-x   - hadoop hadoop          0 2015-05-08 12:39 .Trash/Current/user/hadoop/delete/test1

所以test1删除后被放到了回收站,而test2则被永久删除了。

减少副本数

当一个文件的副本数降低后, NameNode会找到可以被删除的多余的副本。DataNode会在下次心跳的时候拿到这些信息,然后会删除相应的数据块从而空出更多的空间。但需注意, 调用API到实际存储空间释放会有延迟。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论