Skip to content

HDFS分布式文件系统

4731字约16分钟

大数据关系型数据库

2024-06-25

如果你也是一名大数据方向的工作者, 那么就一定听说过分布式文件系统(HDFS). 本文将介绍HDFS、详细阐述其中的重要概念、体系架构、存储原理和读写过程, 如果你希望从零开始了解这个系统, 那么可以阅读本篇文章.

简介

为了解决大数据量下的高效存储问题, 谷歌开发了分布式文件系统(Google File System, GFS), 通过网络实现文件在多态及其上的分布式存储. 而Hadoop分布式文件系统(Hadoop Distributed File System, HDFS)则是针对GFS的开源实现, 和MapReduce一起成为Haddop两大核心组成部分. 总的而言, HDFS实现了以下目标:

  1. 兼容廉价的硬件设备: HDFS将硬件节点失效的情况视为"正常"情况, 设计了快速检测应急教案故障和自动恢复的机制. 也开源持续监视, 容错处理等. 可以实现在硬件出错的情况下也能实现数据的完整性.
  2. 流数据读写
  3. 大数据集
  4. 简单的文件模型: HDFS采用"一次写入、多次读取"的简单文件模型, 文件一旦完成写入并关闭后就只能读取.
  5. 强大的跨平台兼容: HDFS采用Java语言实现, 具有良好的跨ing太兼容, 任何支持JVM的机器都可以运行HDFS.

但是HDFS也有一定的局限性:

  1. 不适合低延迟的访问: HDFS主要面对大规模批量处理的数据, 采用流式数据读取, 具有很高的吞吐率, 但是同时意味着有较高的延迟.
  2. 无法高效存储大量小文件: HDFS一个块的较大, 而文件大小如果小于一个块则无法高效存储. 具体原因在文章后面有具体的讨论.
  3. 不支持多用户写入以及任意修改文件: HDFS只允许一个文件的写入者, 不允许多个用户对同一个文件进行写操作. 并且无法执行随机写操作, 只能追加.

相关概念

HDFS中有几个非常关键的概念, 包括: 块、名称节点、数据节点、第二名称节点.

HDFS和传统文件系统一样, 将文件分为了不同的块, 默认的一个块的大小是 64MB. 可以看出相对于传统文件系统的几千字节, HDFS的块要大得多. 这么做的优势是可以 最小化寻址开销. 采用抽象的块概念的好处:

  1. 支持大规模文件存储: 大规模文件被分割成不同的块, 可以分别存储在不同的机器上, 不受节点本地存储容量的限制.
  2. 简化系统设计: 首先, 因为块的大小是固定的, 简化了存储管理. 其次, 元数据与文件库不需要一起存储, 方便了元数据管理.
  3. 适合数据备份

名称节点和数据节点

在HDFS中有两种节点: 名称节点(Name Node)数据节点(Data Node).

名称节点负责管理分布式文件系统的命名空间(Namespace), 保存了两个核心的数据结构: FsImage和EditLog. 其中FsImage用于维护文件系统树以及文件树中所有的文件和文件夹的元数据. EditLog中记录了所有针对文件的创建、删除、重命名等操作.

数据节点是HDFS的工作节点, 负责数据的存储和读取, 会根据客户端或者名称节点的调度来进行数据的存储和检索, 并向名称节点定期发送自己存储的块的列表. 每个数据节点的数据会被保存在各自节点的本地Linux文件系统中.

FsImage和EditLog是如何工作的?

名称节点在启动时会将FsImage的内容加载进入内存, 然后执行EditLog中的所有操作使得内存中的所有元数据保持最新的状态. 在完成这一切后, 名称节点会创建一个新的FsImage文件和一个空的EditLog文件. 此时名称节点启动成功, 进入正常工作状态.

此后HDFS的更新操作都会写入EditLog. 不写入FsImage的原因是FsImage的大小通常很大(一般在GB以上), 如果所有更新操作都写入FsImage的话更新操作会很缓慢. 而EditLog通常非常小, 可以高效写入.

我们称名称节点在启动过程中加载FsImage和EditLog的过程为"安全模式", 只能对外提供读操作, 无法提供写操作. 启动结束后, 退出"安全模式", 进入正常运行状态后对外提供读写操作.

第二名称节点

前面已经解释过FsImage和EditLog的作用, 显然, 随着名称节点不断运行, EditLog也会变得越来越大, 使得写入的过程变得异常缓慢. 不仅如此, 当节点重启时需要重新执行所有的EditLog中的操作, EditLog如果过大会导致名称节点处于"安全模式"的时间过长, 无法正常对外界提供写操作, 影响用户的正常使用.

为了有效解决EditLog过大带来的一系列问题, HDFS在设计中采用了第二名称节点(Secondary NameNode). 它具有两个功能:

  1. 完成EditLog和FsImage的合并. 减少EditLog的大小, 缩短名称节点重启时间.
  2. 作为名称节点的"检查点", 保存名称节点的元数据信息.

我们现在逐一解释这两个功能:

EditLog和FsImage的合并

  1. 每隔一段时间, 第二名称节点会和名称节点通信, 请求其停止EditLog文件(假设此刻为t1t_1). 此时名称节点会暂时将新达到的写操作添加到新文件EditLog.new中.
  2. 第二名称节点把名称节点中的FsImage文件和EditLog文件拉回到本地, 再加载到内存中并在内存中对这两个文件进行合并操作, 即在内存中逐一执行EditLog使得FsImage处于最新状态.
  3. 合并结束后, 第二名称节点会把合并后的FsImage文件发回名称节点.
  4. 名称节点会用新的FsImage替换原先旧的FsImage文件, 同时用EditLog.new替换EditLog(假设此刻为t2t_2).

通过上述的操作我们有效地减少了EditLog的大小.

作为名称节点的检查点

从上面的合并操作中我们可以得知: 第二名称节点会定期与名称节点通信并拉取EditLog和FsImage文件. 所以我们可以将第二名称节点看作是名称节点的"检查点", 周期性地备份名称节点中的元数据信息. 当名称节点发生故障, 我们可以使用第二名称节点中的元数据进行系统恢复.

但是显然, 在第二名称节点合并操作得到新的FsImage文件并不包含t1t_1t2t_2时刻之间发生的更新操作, 所以如果名称节点在t1t_1t2t_2时刻之间发生故障, 系统就会丢失一部分元数据信息. 在HDFS中也不支持系统直接切换到第二名称节点, 所以第二名称节点只是一个检查点, 并不会达到热备份的效果, 名称节点的元数据仍然有数据丢失的风险.

第二名称节点工作过程示意图

HDFS体系结构

在本小节我们将简要介绍HDFS的体系结构, 然后介绍命名空间管理、通信协议、客户端, 最后会讨论HDFS体系的局限性.

概述

HDFS采用的是一个主从结构(Master/Slave)模型, 一个HDFS集群包含一个名称节点(有且仅有一个)和若干个数据节点.

名称节点作为中心服务器, 负责管理文件系统的命名空间以及客户端对文件的访问. 数据节点一般是一个节点运行一个数据节点进程, 负责文件系统客户端的读写请求, 在名称节点的统一调度下进行数据块的创建、删除和复制等操作. 每个数据节点的数据事实上保存在其本地的Linux文件系统中. 数据节点会向名称节点发送心跳来报告自己的状况, 没有按时发送心跳的数据节点会被标注为"宕机", 不会再分配任何I/O请求.

在系统内部, 文件会被切分为若干个数据块, 这些数据块会被分布到若干个数据节点上. 当客户端需要访问一个文件时, 首先把文件名发送给名称节点, 名称节点会根据文件名找到对应的所有数据块, 在根据每个数据块的信息找到实际存储这些数据块的数据系欸但为止, 并把数据节点位置返还给客户端. 最后客户端直接访问这些数据节点获取数据. 可以看到, 在这个过程中名称节点并不直接参与数据的传输.

命名空间管理

HDFS的命名空间包括: 目录、文件和块. 命名空间管理是指: 命名空间支持对HDFS中的目录、文件和块进行类似文件系统的创建、修改和删除等基本操作.

当前的HDFS体系结构中, 整个HDFS集群只有一个命名空间, 并且只有唯一一个命名节点, 该节点负责对这个命名空间进行管理.

局限

目前HDFS还未实现磁盘配额和文件访问权限等功能, 也不支持文件的硬链接[1]和软连接[2].

通信协议

HDFS的通信协议都是构建在TCP/IP协议上的. 客户端通过一个可配置的端口向名称节点发起TCP链接, 并使用客户端协议与名称节点进行通信. 名称节点和数据节点之间则使用数据节点的协议进行通信. 客户端与数据节点使用RPC(Remote Procedure Call)来实现.

客户端

客户端是用户操作HDFS最常用的方法, 不过严格来说客户端不算是HDFS的一部分. 可以支持打开、读取、写入等常见操作, 也可以使用shell命令来访问数据.

局限性

HDFS只有一个名称节点在简化系统设计的同时带来了一些问题:

  1. 命名空间的限制: 名称节点是保存在内存中的, 受到硬件内存的限制
  2. 性能瓶颈: 整个分布式系统的吞吐量受到单个名称节点的吞吐量的限制
  3. 隔离问题: 由于集群中只有一个名称节点, 只有一个命名空间, 无法对不同的程序进行隔离
  4. 集群可用性: 一旦名称节点发生故障, 会导致整个集群不可用

存储原理

数据的冗余存储

为了保证系统的容错性和可用性, HDFS采用了多副本方式对数据进行冗余存储, 通常一个数据块的多个副本会被分布到不同的数据节点上. 这种多副本方法有以下优点:

  1. 加快数据传输: 当多个客户端需要访问同一个文件时, 可以让客户端从不同的副本中读取数据
  2. 容易检查数据错误
  3. 保证数据可靠性

数据存取策略

数据的存取策略是分布式文件系统的核心内容, 很大程度上会影响到整个分布式文件系统的读写性能.

1. 数据存放

HDFS采用了以机架为基础的数据存放策略.

节点在同一个机架和不同机架的区别

不同机架之间的数据通信需要经过交换机或者路由器, 同一个机架中不同机器之间的通信不需要经过交换机和路由器.

这意味着同一个机架中不同机器之间的通信要比不同机架之间的通信带宽大.

HDFS默认策略是每个数据节点都在不同的机架上.

优点:

  1. 高可靠性. 一个机架出现故障可以使用其他机架上的副本
  2. 读取速度高. 多个副本可以并行读取
  3. 更容易实现负载均衡和错误处理.

缺点:

  1. 写入数据时无法充分利用同一个机架内部的带宽.

HDFS默认的复制因子是3, 每个文件块都会被同时复制到三个地方, 其中有两个副本放在同一个机架的不同机器上, 第三个副本放在不同机架的机器上.

2. 数据读取

HDFS提供了一个API可以确定一个数据节点所属的机架ID, 客户端可以调用API获取自己所属的机架ID. 当客户端读取数据时, 从名称节点获取数据块不同副本的存放位置列表, 可以调用API来确定客户端和这些数据节点所属的机架ID. 如果在同一个机架, 则可以优先读取该副本.

3. 数据复制

HDFS数据复制采用了流水线复制的策略. 当客户端写入一个文件时:

  1. 首先文件被写入本地, 并被切分成不同的数据块
  2. 每个块都向名称节点发起写请求
  3. 名称节点根据数据节点使用情况选择一个数据节点列表返回给客户端
  4. 客户端将数据首先写入列表的第一个数据节点, 并将列表传递给第一个数据节点
  5. 第一个数据节点接受到4KB数据时, 写入本地, 并向列表的第二个数据节点发起连接请求, 将自己已经接受到的4KB数据传给第二个数据节点
  6. 第二个数据节点也以此类推, 在接收到4KB时向第三个数据节点进行请求, 形成流水线 当文件写完的时候, 数据复制也会同时完成.

数据错误与恢复

HDFS拥有非常高的容错性, 使得它可以兼容廉价的硬件设备. HDFS将硬件设备出错堪称时一种常态而非异常, 并设计了相应的机制检测数据错误和进行自动恢复. 分为以下三种情况:

  1. 名称节点出错 名称节点保存着所有的元数据信息, 其中包含最核心的EditLog和FsImage, 如果发生损坏则整个HDFS实例失效.

Hadoop采用两种机制来确保名称节点的安全:

  1. 把名称节点的元数据信息同步存储到其他文件系统. 比如远程挂载NFS

  2. 运行一个第二名称节点

  3. 数据节点出错 每个数据节点会定期向名称节点发送"心跳"信息, 向名称节点报告自己的状态. 当数据节点发生故障或者网路出现断网时, 名称节点会把收不到"心跳"的数据节点标记为"宕机", 节点上所有的数据都标记为"不可读", 名称节点后续不会再发送任何I/O请求.

而标注完"宕机"后, 会有部分数据块的副本数量小于冗余因子, 此时名称节点会定期检测这种情况, 一旦发现则会启动数据冗余复制.

  1. 数据出错 网络传输和磁盘都会导致数据错误. 客户端在读取数据后会采用 md5和shal 对数据进行校验, 以确保读到的时正确的数据. 而此处校验的信息是客户端在文件被创建的时候写入同一个路径的隐藏文件夹下的.

当客户端校验出错, 客户端会请求其他数据节点的数据块副本, 并向名称节点报告该数据块有错误, 名称节点会定期检查并重新复制这个数据块.


  1. 硬链接: 硬链接实际上是同一文件系统中同一个文件的多个"入口". 每个硬链接指向的是同一个文件的inode(索引节点), 所有硬链接共享相同的inode和数据块. 这意味着, 无论你通过哪个硬链接访问文件, 实际上看到的都是同一份数据. 当你修改其中一个硬链接对应的文件内容时, 其他所有硬链接所指的文件内容也会随之改变. 删除任意一个硬链接并不会影响其他硬链接或者文件本身, 只有当所有硬链接都被删除(以及没有其他引用)时, 文件内容才会真正被删除. ↩︎

  2. 软链接: 软链接则是一个独立的特殊类型的文件, 它存储的是目标文件或目录的路径. 当你访问软链接时, 系统会跟踪这个路径并解析到真正的目标文件. 因此, 软链接更像是一个指向目标文件的指针或快捷方式. 如果原文件被删除或移动, 软链接将失效, 因为系统找不到其所指向的目标. ↩︎