目前的大数据生态基本都依赖于 Hadoop 生态,其可靠、高效、可伸缩,可以处理 PB 级别的数据。Hadoop 核心组件包括 HDFS、YARN 和 MapReduce。

HDFS——分布式文件存储系统

HDFS(Hadoop Distributed File System) 是 Hadoop 下的分布式文件存储系统,具有高容错、高吞吐量等特性,可以部署在低成本的硬件上。

HDFS 架构

  • HDFS 遵循主/从架构,由单个 NameNode(NN) 和多个 DataNode(DN) 组成:
    • NameNode:负责执行有关文件系统命名空间的操作,例如打开、关闭、重命名等。它同时还负责集群元数据的存储,记录着文件中各个数据块的位置信息。
    • DataNode:负责提供来自文件系统客户端的读写请求,执行块的创建、删除等操作。

  • HDFS 的文件系统命名空间的层次结构与大多数文件系统类似,支持目录和文件的创建、移动、删除等操作,支持配置用户和访问权限,但是不支持软硬链接。NameNode 负责维护文件系统命名空间,并记录变更。

数据复制备份

Hadoop 的设计初衷是运行在廉价的机器上,这意味着硬件是不可靠的。HDFS 提供数据复制机制来保证容错性。HDFS 将每个文件存储为一系列,每个块由多个副本来保证容错,块的大小和复制因子可以自行配置(默认块大小=128M,复制因子=3)。

  • 复制原理:HDFS 一般都会部署在不同机房的多台容器上,通常同一机房容器之间的网络带宽大于跨机房容器之间的网络带宽。因此 HDFS 采用机架感知副本放置策略(可自定义策略)。
    • 当复制因子为 3 :在写入程序位于 DataNode 上时,就优先将写入文件的一个副本放置在该 DataNode 上,否则放在随机 DataNode 上。之后在另一个远程机房上的任意一个节点上放置另一个副本,并在该机房上的另一个节点上放置最后一个副本。此策略可以减少机房间的写入流量,从而提高写入性能。
    • 当复制因子大于 3:随机确定第 4 个和之后副本的放置位置,同时保持每个机房的副本数量低于上限,上限值通常为 (复制系数 - 1)/ 机架数量 + 2,需要注意的是不允许同一个 DataNode 上具有同一个块的多个副本。
  • 副本选择:为了最大限度地减少带宽消耗和读取延迟,HDFS 在执行读取请求时,优先读取距离读取器最近的副本。如果在与读取器节点相同的机房上存在副本,则优先选择该副本。如果 HDFS 群集跨越多个数据中心,则优先选择本地数据中心上的副本。

架构稳定性

  • 心跳机制和重新复制:每个 DataNode 定期向 NameNode 发送心跳消息,如果超过指定时间没有收到心跳消息,则将 DataNode 标记为死亡。NameNode 不会将任何新的 IO 请求转发给标记为死亡的 DataNode,也不会再使用这些 DataNode 上的数据。 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode 会跟踪这些块,并在必要的时候进行重新复制。
  • 数据的完整性:由于存储设备故障等原因,存储在 DataNode 上的数据块也会发生损坏。为了避免读取到已经损坏的数据而导致错误,HDFS 提供了数据完整性校验机制来保证数据的完整性。(当客户端创建 HDFS 文件时,它会计算文件的每个块的校验和,并将校验和存储在同一 HDFS 命名空间下的单独的隐藏文件中。当客户端检索文件内容时,它会验证从每个 DataNode 接收的数据是否与存储在关联校验和文件中的校验和匹配。如果匹配失败,则证明数据已经损坏,此时客户端会选择从其他 DataNode 获取该块的其他可用副本。)
  • 元数据的磁盘故障FsImage(元数据检查点,包含整个 HDFS 的所有目录文件信息) 和 EditLog(写操作记录) 是 HDFS 的核心数据,这些数据的意外丢失可能会导致整个 HDFS 服务不可用。为了避免这个问题,可以配置 NameNode 使其支持 FsImageEditLog 多副本同步,这样 FsImageEditLog 的任何改变都会引起每个副本 FsImageEditLog 的同步更新。
  • 支持快照:快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。

HDFS 特点

  • 高容错:多副本方案
  • 高吞吐:支持高吞吐数据访问而非低延迟
  • 大文件支持:文档大小可达 GB 到 TB 级别
  • 简单一致性模型:更适合一次写入多次读取 (write-once-read-many) 的访问模型,支持将内容追加到文件末尾,但不支持数据的随机访问,不能从文件任意位置新增数据
  • 跨平台移植性:具有良好的跨平台移植性,大部分大数据计算框架都将其作为数据持久化首选方案

HDFS 写数据原理

角色主要职能:

  • client:文件切块
  • NameNode:为每个数据块分配 DataNode
  • DataNode:通过复制管道存储数据

以客户端需要写入200M数据为例:

  1. 用户向 client 提供 BLOCKSIZE(大文件会被切分成块,通常是 64M 或者 128M) 和 REPLICATION FACTOR(每个数据块会被存储在不同的地方,通常是3个)参数
  2. client 根据参数将大文件切分成两个块(向上取整 :200 / 128 = 2)
  3. client 告知 NameNode 要求写入一个三备份的 128M 数据块
  4. NameNode 分配 DataNode 并按照升序排列告知 client(DataNode1 DataNode2 DataNode3)
  5. client 只将数据(和列表)发送给 DataNode1 存储,然后 DataNode1 将相同的数据传输给 DataNode2,以此类推直到最后一个节点(DataNode3)完成存储
  6. 当前块完成所有节点的存储后,会发送完成信号给 NameNode
  7. NameNode 告知 client 当前数据块被成功存储和备份在 HDFS 中
  8. client 重复 3~7 直到所有的数据块完成传输后,告知 NameNode 所有数据块已完成写入并请求关闭文件

HDFS 读数据原理

  1. client 向 NameNode 通过文件名请求文件
  2. NameNode 向 client 回复这个文件的所有数据块的列表和每个数据块所对应的 DataNode 的列表(按距离客户端远近排列)
  3. client 按顺序下载数据块,向每个块对应的最近 DataNode(列表中的第一个)下载数据

HDFS 故障类型和检测方法

由于 NameNode 挂掉基本全军覆没,所以一般主要关注 DataNode 的故障检测。

  • 故障1:节点故障
    • 故障检测:心跳机制(每3s发一次给 NameNode,NameNode 10min没收到就认为死亡)会排除死亡的 DataNode
  • 故障2:通讯故障(无法收发)
    • 故障检测:每次 client 发送数据给 DataNode 时,接收者都会回复一个应答信号。有重试机制,多次失败后 client 就会认为 DataNode 挂掉或者网络故障
  • 故障3:数据损坏
    • 故障检测:client 在发送数据时头部会包含校验和,且数据和校验和会被同时存储。所有的 DataNode 定时发送数据块报告给 NameNode。在发送报告之前 DataNode 会检测校验和是否正常,不会发送损坏的数据块信息,因此 NameNode 根据数量 diff 就可得知有多少数据损坏

故障处理

  • 写入故障:client 在向 DataNode 写入数据的时候会接收应答信号,如果收不到某个 DataNode 的信号,client 就会调整通道跳过此节点。此时这个数据块没有被充分备份,NameNode 会稍后处理(详见 DataNode 故障)
  • 读取故障:如果最近的 DataNode 挂掉,client 就会从次近的节点上读取数据
  • DataNode 故障
    • NameNode 维护了两张表,一个是数据块列表(记录每个数据块存储在哪些 DataNode 上),一个是 DataNode 列表(记录每个 DataNode 存储了哪些数据块)。如果 NameNode 发现一个 DataNode 上的数据块已经损坏,则会更新数据块表(将此 DN 移除出该表);如果发现是某个 DataNode 挂掉,则会同时更新两张表。
    • 未充分备份的数据块处理:NameNode 通过定时扫描数据块列表就可以得知未充分备份的数据块,其可以要求未有备份的新 DataNode 去已有备份的 DataNode 拷贝数据块,使数据块充分备份。不过 HDFS 不保证至少存活一份数据,只负责尽可能地选择合理的备份位置。

YARN——集群资源管理器

Apache YARN(Yet Another Resource Negotiator)是 hadoop 2.0 引入的集群资源管理系统。用户可以将各种服务框架部署在 YARN 上,由 YARN 进行统一地管理和资源分配。

YARN 架构

  • ResourceManagerResourceManager 通常在独立的机器上以后台进程的形式运行,它是整个集群资源的主要协调者和管理者。ResourceManager 负责给用户提交的所有应用程序分配资源,它根据应用程序优先级、队列容量、ACLs、数据位置等信息,做出决策,然后以共享的、安全的、多租户的方式制定分配策略,调度集群资源。
  • NodeManagerNodeManager 是 YARN 集群中的每个具体节点的管理者。主要负责该节点内所有容器的生命周期的管理,监视资源和跟踪节点健康。具体如下:
    • 启动时向 ResourceManager 注册并定时发送心跳消息,等待 ResourceManager 的指令
    • 维护 Container 的生命周期,监控 Container 的资源使用情况
    • 管理任务运行时的相关依赖,根据 ApplicationMaster 的需要,在启动 Container 之前将需要的程序及其依赖拷贝到本地
  • ApplicationMaster:在用户提交一个应用程序时,YARN 会启动一个轻量级的进程 ApplicationMasterApplicationMaster 负责协调来自 ResourceManager 的资源,并通过 NodeManager 监视容器内资源的使用情况,同时还负责任务的监控与容错。具体如下:
    • 根据应用的运行状态来决定动态计算资源需求
    • ResourceManager 申请资源,监控申请的资源的使用情况
    • 跟踪任务状态和进度,报告资源的使用情况和应用的进度信息
    • 负责任务的容错
  • ContainerContainer 是 YARN 中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等。当 AM 向 RM 申请资源时,RM 为 AM 返回的资源是用 Container 表示的。YARN 会为每个任务分配一个 Container,该任务只能使用该 Container 中描述的资源。ApplicationMaster 可在 Container 内运行任何类型的任务。例如,MapReduce ApplicationMaster 请求一个容器来启动 map 或 reduce 任务,而 Giraph ApplicationMaster 请求一个容器来运行 Giraph 任务。

YARN 工作原理简述

  1. Client 提交作业到 YARN 上
  2. Resource Manager 选择一个 Node Manager,启动一个 Container 并运行 Application Master 实例
  3. Application Master 根据实际需要向 Resource Manager 请求更多的 Container 资源(如果作业很小, 应用管理器会选择在其自己的 JVM 中运行任务)
  4. Application Master 通过获取到的 Container 资源执行分布式计算

MapReduce

Hadoop MapReduce 是一个并行分布式计算框架,用于编写批处理应用程序。编写好的程序可以提交到 Hadoop 集群上用于并行处理大规模的数据集。

MapReduce 主要有三个阶段:

  • Map 阶段:map 或者 mapper 将输入的数据集拆分成独立块。通常这些输入数据会存放在 HDFS,输入文件会被逐行输入到 mapper 函数中,mapper 函数会并行处理并创建一些数据块。
  • Shuffle 阶段:Map 转换到 Reduce 的中间过程,一般有 partitions、sort、combine 等操作
  • Reduce 阶段:对各个数据块上的数据并行运算,输出最终结果
1
(input) <k1, v1> -> map -> <k2, v2> -> shuffle -> <k2, v2> -> reduce -> <k3, v3> (output)

MapReduce 具体流程

以基本的词频统计为例:

  • input:读取文件
  • splitting:文件按行拆分,K1 为行数,V1 为对应行的文本内容
  • mapping:每行按空格拆分,拆分得到 List(K2, V2),K2 代表每个单词,V2 代表出现次数
  • shuffling:由于 Mapping 操作可能分发到不同的节点上并行运算,因此 shuffling 需要把相同 key 的数据转发到相同节点上合并。K2 为每个单词,List(V2) 为可迭代集合,V2 为 Mapping 中的 V2
  • reducing:对 List(V2) 进行规约求和,最终输出词频统计结果

MapReduce 编程模型中 splittingshuffing 操作都是由框架实现的,需要我们自己编程实现的只有 mappingreducing,这也就是 MapReduce 这个称呼的来源。

Combiner, Partitioner and Sort

Combiner、 Partioner 和 Sort 是 map 运算之后的可选操作,他们属于 shuffle 阶段的实现方式。

  • Combiner:执行本地化的 reduce 操作,在 map 计算之后先简单地在每个节点本地进行重复 key 的合并

  • Partitioner:分类器,将每个 map 的输出结果分发到新的节点上,具有相同 key 值的数据会分发到同一节点

  • Sort:key 有序排列,一般结合了 shuffle 操作