Hadoop核心组件
目前的大数据生态基本都依赖于 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 使其支持FsImage和EditLog多副本同步,这样FsImage或EditLog的任何改变都会引起每个副本FsImage和EditLog的同步更新。 - 支持快照:快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。
 
HDFS 特点
- 高容错:多副本方案
 - 高吞吐:支持高吞吐数据访问而非低延迟
 - 大文件支持:文档大小可达 GB 到 TB 级别
 - 简单一致性模型:更适合一次写入多次读取 (write-once-read-many) 的访问模型,支持将内容追加到文件末尾,但不支持数据的随机访问,不能从文件任意位置新增数据
 - 跨平台移植性:具有良好的跨平台移植性,大部分大数据计算框架都将其作为数据持久化首选方案
 
HDFS 写数据原理

角色主要职能:
- client:文件切块
 - NameNode:为每个数据块分配 DataNode
 - DataNode:通过复制管道存储数据
 
以客户端需要写入200M数据为例:
- 用户向 client 提供 BLOCKSIZE(大文件会被切分成块,通常是 64M 或者 128M) 和 REPLICATION FACTOR(每个数据块会被存储在不同的地方,通常是3个)参数
 - client 根据参数将大文件切分成两个块(向上取整 :200 / 128 = 2)
 - client 告知 NameNode 要求写入一个三备份的 128M 数据块
 - NameNode 分配 DataNode 并按照升序排列告知 client(DataNode1 DataNode2 DataNode3)
 - client 只将数据(和列表)发送给 DataNode1 存储,然后 DataNode1 将相同的数据传输给 DataNode2,以此类推直到最后一个节点(DataNode3)完成存储
 - 当前块完成所有节点的存储后,会发送完成信号给 NameNode
 - NameNode 告知 client 当前数据块被成功存储和备份在 HDFS 中
 - client 重复 3~7 直到所有的数据块完成传输后,告知 NameNode 所有数据块已完成写入并请求关闭文件
 
HDFS 读数据原理
- client 向 NameNode 通过文件名请求文件
 - NameNode 向 client 回复这个文件的所有数据块的列表和每个数据块所对应的 DataNode 的列表(按距离客户端远近排列)
 - 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 架构

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

Client提交作业到 YARN 上Resource Manager选择一个Node Manager,启动一个Container并运行Application Master实例Application Master根据实际需要向Resource Manager请求更多的Container资源(如果作业很小, 应用管理器会选择在其自己的 JVM 中运行任务)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 编程模型中 splitting 和 shuffing 操作都是由框架实现的,需要我们自己编程实现的只有 mapping 和 reducing,这也就是 MapReduce 这个称呼的来源。
Combiner, Partitioner and Sort
Combiner、 Partioner 和 Sort 是 map 运算之后的可选操作,他们属于 shuffle 阶段的实现方式。
Combiner:执行本地化的 reduce 操作,在 map 计算之后先简单地在每个节点本地进行重复 key 的合并
Partitioner:分类器,将每个 map 的输出结果分发到新的节点上,具有相同 key 值的数据会分发到同一节点
Sort:key 有序排列,一般结合了 shuffle 操作
