案例研究:Hadoop 分布式文件系统 (HDFS)

已完成

MapReduce 编程模型允许按照两个函数来构造计算作业:映射和化简。 输入作为键值对送入 MapReduce 中,然后通过映射函数进行处理并送入到化简函数中。 化简运算随后会生成结果,该结果也采用键值对形式。 MapReduce 设计为可在大型计算群集上并行执行映射和化简运算的许多实例。 MapReduce 编程模型在后面的模块中进行了详细介绍。

MapReduce 编程模型假设有一个分布式存储系统可用,它可在群集的所有节点间使用,具有单个命名空间(分布式文件系统 (DFS) 所在的位置)。 DFS 与 MapReduce 群集的节点并置。 DFS 设计为与 MapReduce 协同工作,为整个 MapReduce 群集维护单个命名空间。

MapReduce 的一个开放源代码版本名为 Apache Hadoop2,在大数据圈中非常受欢迎。 HDFS 是开放源代码 DFS。 HDFS 设计作为可缩放的分布式容错文件系统,主要满足 MapReduce 编程模型的需求。 视频 4.12 介绍了 HDFS。

必须注意的是,HDFS 不与 POSIX 兼容,并且它本身不是可装载的文件系统。 通常通过 HDFS 客户端或使用 Hadoop 库中的应用程序编程接口 (API) 调用来访问 HDFS。 但是,通过为 (HDFS) 开发用户空间文件系统 (FUSE) 驱动程序,可以将它作为虚拟设备装载到类似于 UNIX 的操作系统中。

HDFS 体系结构

如前所述,HDFS 是一种 DFS,设计为在节点群集上运行,其体系结构设计具有以下目标:

  • 单一的群集范围公共命名空间
  • 能够存储大文件(例如 TB 级或 PB 级)
  • 支持 MapReduce 编程模型
  • 流数据访问,用于写入一次、多次读取数据访问模式
  • 使用商用硬件实现高可用性

下图说明了 HDFS 群集:

HDFS architecture.

图 1:HDFS 体系结构

HDFS 遵循主从设计。 主节点称为 NameNode。 NameNode 为整个群集处理元数据管理,并为 HDFS 上存储的所有文件维护单个命名空间。 从节点称为 DataNode。 DataNode 将实际数据块存储在每个节点中的本地文件系统上。

HDFS 中的文件拆分为各个块(也称为区块),默认大小为每个 128MB。 相反,本地文件系统的块大小通常大约为 4KB。 HDFS 使用较大的块大小,因为它设计为通过可高效处理 MapReduce 作业的方式来存储非常大的文件。

默认情况下,MapReduce 中的单个映射任务配置为独立地处理单个 HDFS 块,因此多个映射任务可以并行处理多个 HDFS 块。 如果块大小太小,则需要在群集的节点间分布大量映射任务,这样做的开销可能会对性能产生负面影响。 另一方面,如果块太大,可以并行处理文件的映射任务的数量便会减少,从而影响并行度。 HDFS 允许按文件指定块大小,因此用户可以调整块大小以实现所需并行度级别。 在后面的模块中详细讨论了 MapReduce 与 HDFS 的交互。

此外,由于 HDFS 设计为可容忍单个节点的故障,因此数据块会在节点间进行复制,以提供数据冗余。 以下部分详细说明了此过程。

HDFS 中的群集拓扑

Hadoop 群集通常部署在数据中心内,其中包含多个机架的使用胖树拓扑(在前面的模块中进行了讨论)连接的服务器。 为此,HDFS 设计为可感知群集拓扑,这有助于进行块放置决策以影响性能和容错。 常见 Hadoop 群集的每个机架有大约 30 到 40 台服务器,具有专用于机架的千兆交换机,以及指向核心交换机或路由器的上行链路(其带宽在数据中心的许多机架间共享),如下图所示:

HDFS cluster topology.

图 2:HDFS 群集拓扑

需要注意的重点是,Hadoop 假设机架中节点的聚合带宽高于不同机架上节点间的聚合带宽。 当涉及到数据访问和副本放置(在以下部分中进行讨论)时,Hadoop 的设计中会采用此假设。

当在群集上部署 HDFS 时,系统管理员可以使用将每个节点映射到群集中特定机架的拓扑描述进行配置。 网络距离按跃点进行度量,其中一个跃点对应于拓扑中的一个链接。 Hadoop 采用树样式拓扑,两个节点之间的距离是它们与最接近的公共上级的距离之和。

在图 2 的示例中,节点 1 与本身之间的距离是零个跃点(两个进程在同一个节点上进行通信的情况)。 节点 1 与节点 2 之间的距离是两个跃点,而节点 3 与节点 4 之间的距离是四个跃点。

以下视频演示 HDFS 中的文件读取和写入操作。

File reads in HDFS.

图 3:HDFS 中的文件读取

图 3 说明 HDFS 中的文件读取过程。 在文件打开进行读取时,HDFS 客户端(需要访问文件的实体)首先联系 NameNode。 NameNode 随后向客户端提供文件块位置的列表。 Hadoop 还假设块在节点间进行复制,因此在提供特定块的位置时,NameNode 实际上会查找与客户端最近的块。 位置按以下顺序确定(“递减位置”):客户端所在的同一个节点中的块、客户端所在的同一个机架中的块以及客户端所在的机架外部的块。

确定块位置后,客户端将启动与每个 DataNode 的直接连接,将数据从 DataNode 流式传输给客户端进程,此过程是在 HDFS 客户端 调用对数据块的读取操作时完成的。 因而不必传输整个块,客户端便可开始计算,从而使计算和通信错开。 客户端读取完第一个块后,它会对其余块重复此过程,直到客户端读取完所有块,然后关闭文件。

务必要注意的是,客户端会直接与 DataNode 联系以检索数据。 这种联系使 HDFS 可以扩展到大量并发客户端,以便同时并行读取数据。

在 HDFS 中,文件写入与文件读取不同(图 4)。 需要将数据写入 HDFS 的客户端会首先联系 NameNode,随后向它通知创建文件。 NameNode 会检查文件是否已存在,并验证客户端是否拥有创建文件的权限。 如果检查通过,则 NameNode 会创建新文件的记录。

File writes in HDFS.

图 4:HDFS 中的文件写入

客户端随后继续将文件写入内部数据队列,并向 NameNode 请求获取集群中 DataNode 上的块位置。 内部队列中的块随后会以管道方式传输到各个 DataNode。 块在第一个 DataNode 上写入,后者随后通过管道将块传输到其他 DataNode,以便写入块的副本。 因而块会在文件写入期间进行复制。 必须注意的是,HDFS 不会向客户端确认写入(图 4.28 中的步骤 5),直到该文件的所有副本都已由 DataNode 写入。

Hadoop 还在副本放置期间使用机架位置的概念。 默认情况下,数据块在 HDFS 中进行三次复制。 HDFS 尝试将第一个副本放置在正在写入块的客户端所在的同一个节点上。 如果客户端进程未在 HDFS 群集中运行,则会随机选择节点。 第二个副本会写入到所处机架与第一个节点不同(机架外)的节点。 块的第三个副本随后会写入到所处机架与第二个节点相同的另一个随机节点。 更多副本会写入到群集中的随机节点,但系统会尝试避免将过多副本放置在同一个机架上。 图 5 说明了 HDFS 中三次复制块的副本放置。 HDFS 副本放置背后的理念是能够容忍节点和机架故障。 例如,当整个机架由于电源或网络问题而进入离线状态时,仍可以在另一个机架中找到请求的块。

Replica placement for a triple-replicated block in HDFS.

图 5:HDFS 中三次复制块的副本放置

同步:语义

HDFS 的语义发生了一点变化。 早期版本的 HDFS 遵循严格的不可变语义。 在早期版本的 HDFS 中写入文件后,便无法再重新打开以进行写入。 仍可以删除文件。 不过当前版本的 HDFS 支持以有限的方式进行追加。 这仍然十分有限,因为现有二进制数据一旦写入到 HDFS,便无法进行修改。

选择在 HDFS 中进行这种设计是因为某些最常见的 MapReduce 工作负载遵循一次写入、多次读取数据访问模式。 MapReduce 是具有预定义阶段的受限计算模型,MapReduce 中的化简器的输出会将独立文件作为输出写入到 HDFS。 HDFS 侧重于一次有多个客户端同时进行快速读取访问。

一致性模型

HDFS 是具有强一致性的文件系统。 每个数据块都会复制到多个节点,但只有在所有副本都已成功写入之后,才会声明写入成功。 因此,所有客户端都应在文件写入后立即看到文件,并且文件在所有客户端上的视图都相同。 HDFS 的不可变语义使得这一点相对容易实现,因为在文件的生存期内文件只能打开进行写入一次。

HDFS 中的容错

HDFS 中的主要容错机制是复制。 如前所述,默认情况下,写入 HDFS 的每个块都会复制三次,但用户可以根据需要按文件更改这一点。

NameNode 通过检测信号机制跟踪 DataNode。 每个 DataNode 将定期检测信号消息(每隔几秒)发送到 NameNode。 如果 DataNode 处于不活动状态,则发送到 NameNode 的检测信号会停止。 如果错过的检测信号消息数达到某个阈值,则 NameNode 会检测到 DataNode。 NameNode 随后将 DataNode 标记为不活动,不再将任何 I/O 请求转发到该 DataNode。 存储在该 DataNode 上的块应在其他 DataNode 上具有其他副本。 此外,NameNode 对文件系统执行状态检查以发现复制不足的块,并执行群集重新平衡过程,以便为副本数量少于所需数量的块启动复制。

NameNode 是 HDFS 中的单一故障点 (SPOF),因为 NameNode 的故障会使整个文件系统关闭。 NameNode 在内部维护两个用于存储文件系统状态的磁盘上数据结构:映像文件和编辑日志。 映像文件是文件系统元数据在某个时间点的检查点,而编辑日志是自上次创建映像文件以来,文件系统元数据的所有事务的日志。 对文件系统元数据的所有传入更改都会写入到编辑日志。 编辑日志和映像文件会定期进行合并以创建新映像文件快照,并且编辑日志会被清除。但在 NameNode 发生故障时,元数据将不可用,NameNode 上的磁盘故障会是灾难性的,因为文件元数据会丢失。

为了备份 NameNode 中的元数据,HDFS 允许创建辅助 NameNode,后者会定期从 NameNode 复制映像文件。 这些副本有助于在 NameNode 上发生数据丢失时恢复文件系统,但 NameNode 编辑日志中的最后几个更改会丢失。 最新版本 Hadoop 中正在进行的工作旨在创建真正丰富的辅助 NameNode,它会在 NameNode 发生故障时自动接管。

HDFS 实践

虽然 HDFS 主要设计用于通过为映射和化简运算提供 DFS 来支持 Hadoop MapReduce 作业,但是 HDFS 发现了大量与大数据工具一起使用的情况。

HDFS 用于在 Hadoop 框架之上构建的多个 Apache 项目,其中包括 Pig、Hive、HBase 和 Giraph。 其他项目(如 GraphLab)中也包含 HDFS 支持

HDFS 的主要优点包括以下这些:

  • MapReduce 工作负载的高带宽:众所周知,大型 Hadoop 群集(数千台机器)可使用 HDFS 以高达每秒 1TB 的速率连续写入数据
  • 高可靠性:容错是 HDFS 中的主要设计目标。 HDFS 复制可提供较高的可靠性和可用性,尤其是在大型群集中(磁盘和服务器的故障概率明显更高)。
  • 每个字节的低成本:与专用共享磁盘解决方案(如 SAN)相比,每 GB 的 HDFS 成本更低,因为存储与计算服务器并置。 使用 SAN 时,必须为托管基础结构(如磁盘阵列机箱和更高级别的企业磁盘)支付额外费用以管理硬件故障。 HDFS 设计为通过商用硬件运行,并且冗余采用软件进行管理以容忍故障。
  • 可伸缩性:HDFS 允许将 DataNodes 添加到正在运行的群集,并提供工具以在添加群集节点时手动重新平衡数据块(可以在不关闭文件系统的情况下进行)。

HDFS 的主要缺点包括以下这些:

  • 小文件低效:HDFS 设计为用于较大的块大小(64MB 及更大)。 这是为了采用大文件(数百 MB、GB 或 TB)并将它们分为各个块,随后可以将块送入 MapReduce 作业以并行处理。 当实际文件大小较小(在 KB 范围内)时,HDFS 十分低效。 具有大量小文件会对 NameNode 施加额外压力,后者必须维护文件系统中所有文件的元数据。 通常,HDFS 用户使用诸如序列文件之类的技术将许多小文件合并为较大的文件。
  • 不与 POSIX 兼容:HDFS 并未设计成为 POSIX 兼容的可装载文件系统;应用程序必须从头开始编写或进行修改才能使用 HDFS 客户端。 存在一些解决方法,使 HDFS 可以使用 FUSE 驱动程序进行装载,但是一旦文件关闭,文件系统语义便不允许对其进行写入。
  • 一次写入模型:对于需要对相同文件进行并发写入访问的应用程序,一次写入模型是一种潜在的缺点。 但是,最新版本的 HDFS 现在支持文件追加。

简而言之,对于遵循 MapReduce 模型或专门编写为使用 HDFS 的分布式应用程序,HDFS 是一个很好的存储后端选择。 HDFS 可以高效地用于少量大文件,而不是大量小文件。


参考

  1. Sanjay Ghemawat, Howard Gobioff, and Shun-Tak Leung (2003). The Google File Systems 19th ACM Symposium on Operating Systems Principles
  2. White, Tom (2012). Hadoop: The Definitive Guide O'Reilly Media, Yahoo Press

知识检查

1.

HDFS 与本地文件系统相比具有什么优点?

2.

HDFS 何时向磁盘提交写入?

3.

HDFS 提供哪种类型的一致性模型?