一、Hadoop简介
1、Hadoop 是什么
官网地址:https://hadoop.apache.org/releases.html
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力,解决海量数据的存储及海量数据的分析计算问题。
- 狭义上的Hadoop指的是其核心三大组件,包括HDFS、YARN及MapReduce。
- HDFS为海量的数据提供了存储
- MapReduce则为海量的数据提供了计算
- YARN则提供统一的资源管理和调度。
- 广义上的Hadoop是指Hadoop的整个技术生态圈;
2、Hadoop 发展史
Hadoop起源于Lucene框架,后其创始人为解决对于海量数据存储困难、检索速度慢的问题,借鉴了Google的大数据神级三大思想,创建了Nutch,后被分离出来,纳入Apache的项目Hadoop中。因此说Google的大数据三大思想是Hadoop的思想之源也不为过。这三大思想分别对应三篇论文:
- The Google File System (对应HDFS)
- MapReduce: Simplified Data Processing on Large Clusters (对应MapReduce)
- Bigtable: A Distributed Storage System for Structured Data (对应HBase)
建议大家都去找这三篇论文进行阅读学习,这对大数据的理解大有裨益,而且最好看英文原版,资源可在网上搜索查找。
3、Hadoop 发行版本
Hadoop目前发展得最好的有三大发行版本:Apache、Cloudera(CDH)、Hortonworks(HDP)。他们都有各自的特点:
- Apache是最原始、最基础的版本,Hadoop项目也是起源于Apache,对于入门学习最好,因此也使得其生态系统开源社区最为活跃。但由于其生态里的各组件版本较为混乱,因此会产生较多依赖和冲突。
- Cloudera最早将Hadoop商用化,比Apache Hadoop在兼容性、安全性、稳定性上有所增强,在大型互联网企业中用得较多。但其安装集群的框架不是开源的,商用服务需要按节点收费。
- Hortonworks开发了很多增强特性并提交至核心主干,使得Apache Hadoop能够在Microsoft Windows平台上本地平稳运行;另外其文档做得较好。但其发展起步最晚,普及度不高,安装和升级较为费劲,其服务也是按节点收费。
我们的介绍基于Apache版本开展。
4、Hadoop 优势
- 高可靠性:Hadoop底层维护多个数据副本,所以即使Hadoop某个计算元素或存储出现故障,也不会导致数据的丢失。
- 高扩展性:能在廉价机器组成的集群间分配任务数据,可以方便地扩展数以千计的节点。
- 高效性:在MapReduce的思想下,Hadoop是并行工作的,可以加快任务的处理速度。
- 高容错性:能够自动将失败的任务重新分配。
5、Hadoop1.x与2.x的区别

- 在Hadoop1.x时代,Hadoop中的MapReduce同时处理业务逻辑运算和资源的调度,耦合性较大。
- 在Hadoop2.x时代,增加了Yarn。Yarn只负责资源的调度,MapReduce只负责分析运算。
- Hadoop3.x在组成上没变化,但在功能和性能方面,对Hadoop内核进行了多项重大改进。
二、HDFS
1、HDFS是什么?
HDFS(Hadoop Distributed File System)是 Hadoop 生态系统的核心分布式文件系统,专为存储超大规模数据集(如 PB 级数据)并支持高吞吐量的数据访问而设计。它基于 Google File System (GFS) 论文思想实现,具备高容错性、可扩展性和低成本的特点,适用于处理海量非结构化数据(如日志、视频、文档等)。
2、HDFS 核心设计目标
- 处理超大文件:适合存储 TB/PB 级文件。
- 运行在廉价硬件:通过冗余机制保障可靠性。
- 高吞吐量:适合批处理,而非低延迟访问。
- 流式数据访问:一次写入、多次读取(WORM 模型)。
- 高容错性:自动处理节点故障。
3、HDFS 架构
HDFS 采用 主从架构,主要包含以下组件:
- NameNode(主节点)
- 角色:管理文件系统的元数据(metadata)。
- 职责:
- 维护文件系统的目录树结构(文件名、权限、块列表)。
- 记录文件与数据块(Block)的映射关系(如 file.txt -> block1, block2)。
- 管理 DataNode 的心跳和块报告,确保数据副本数量。
- 存储信息:
- FsImage:文件系统元数据的持久化快照(存储在磁盘)。
- EditLog:记录所有文件系统更改操作(如创建、删除文件)。
- 单点问题:早期版本中 NameNode 是单点,Hadoop 2.x 后支持 高可用(HA),通过 Active/Standby NameNode 解决。
- DataNode(从节点)
- 角色:实际存储数据块。
- 职责:
- 存储文件数据块(默认大小 128MB 或 256MB)。
- 响应客户端的读写请求。
- 定期向 NameNode 发送心跳和块报告。
- 执行数据块的创建、删除和复制(根据 NameNode 指令)。
- Secondary NameNode(辅助节点)
- 职责:
- 定期合并 FsImage 和 EditLog,生成新的 FsImage,减少 NameNode 重启时间。
- 防止 EditLog 过大导致 NameNode 性能下降。
- 注意
- 不是 NameNode 的热备,而是辅助合并元数据。
4、HDFS 关键特性
- 分块存储(Block)
- 文件被切分为固定大小的块(默认 128MB),分散存储在不同节点。
- 优势:
- 简化存储设计,支持大文件存储。
- 块可并行处理,提升计算效率。
- 多副本机制(Replication)
- 每个块默认保存 3 个副本,存储策略如下:
- 第1副本:写入客户端所在节点(若为集群内节点)。
- 第2副本:同一机架(Rack)的不同节点。
- 第3副本:不同机架的节点。
- 优势:保障数据可靠性,提升读取效率(就近访问)。
- 配置参数:dfs.replication
- 每个块默认保存 3 个副本,存储策略如下:
5、HDFS 写数据
5.1 写数据流程
1. 客户端发起写入请求
- 步骤 :
- 客户端调用
create()
方法向 NameNode 发起 创建文件请求。 - NameNode 执行以下操作:
- 检查权限:验证客户端是否有写入权限。
- 检查文件是否存在:若文件已存在,返回错误。
- 生成元数据:在内存中创建文件元数据(文件名、块列表等),但此时文件尚未分配数据块。
- 写入 EditLog :将"创建文件"操作记录到 EditLog(持久化到磁盘),确保元数据变更可恢复。
- 响应客户端:返回成功信号,允许写入。
- 客户端调用
- 关键点 :
- EditLog 的写入是同步的:NameNode 必须先将操作写入 EditLog 磁盘,再响应客户端,确保元数据变更的持久化。
- 此时文件为空 :数据块尚未分配,仅元数据被记录。
2. NameNode 分配数据块与 DataNode 列表
- 步骤 :
- 客户端调用
write()
方法开始写入数据。 - 客户端将数据按块(默认 128MB)切分,并请求 NameNode 为第一个块分配存储位置。
- NameNode 执行以下操作:
- 选择 DataNode:根据副本策略(默认 3 副本)和负载均衡策略,选择一组 DataNode(如 DN1、DN2、DN3)。
- 更新元数据:在内存中记录该块与 DataNode 的映射关系。
- 写入 EditLog:将"分配块"操作记录到 EditLog。
- 返回 DataNode 列表:将选中的 DataNode 列表返回给客户端。
- 客户端调用
- 关键点 :
- 副本策略:NameNode 根据机架感知策略(Rack Awareness)选择 DataNode,优先跨机架存储副本。
- EditLog 记录块分配:此时 EditLog 记录了块的元数据变更,但尚未包含实际数据。
- NameNode 不会预先分配所有块的存储位置,而是按需动态分配。
- 每次需要写入新块时,客户端必须重新向 NameNode 请求分配新的 DataNode 列表。
- 当第一个块写满后,客户端向 NameNode 发送
addBlock
请求,NN会重复上述行为。
3. 客户端通过管道(Pipeline)写入数据
- 步骤 :
- 客户端与 DataNode 建立 写入管道(Pipeline) :
- 管道顺序:客户端 → DN1 → DN2 → DN3(假设 3 副本)。
- 客户端将数据块拆分为 数据包(Packet,默认 64KB),依次发送给 DN1。
- 流水线复制 :
- DN1 接收数据包后,存储到本地,并转发给 DN2。
- DN2 同理转发给 DN3。
- 每个 DataNode 将数据包写入本地磁盘的临时文件(
blk_<id>.tmp
)。
- 确认机制 :
- 每个 DataNode 收到数据包后,向上游节点发送确认(ACK)。
- 客户端最终收到所有 DataNode 的确认,表示该数据包写入成功。
- 客户端与 DataNode 建立 写入管道(Pipeline) :
- 关键点 :
- 数据不经过 NameNode:客户端直接与 DataNode 通信,避免 NameNode 成为瓶颈。
- 临时文件 :数据块在完全写入前以临时文件形式存在,写入完成后才提交为正式块。
4. 数据块提交与元数据更新
- 步骤 :
- 当客户端完成一个块的全部数据包写入后,通知 NameNode。
- NameNode 执行以下操作:
- 更新元数据:将块与 DataNode 的映射关系标记为已提交。
- 写入 EditLog:记录"块提交"操作到 EditLog。
- DataNode 将临时文件重命名为正式块文件(如
blk_1234
),并向 NameNode 发送 块报告(BlockReport)。
- 关键点 :
- 最终一致性:NameNode 通过块报告确认所有副本已持久化。
- EditLog 记录提交状态 :确保元数据与实际存储一致。
5. 关闭文件与最终确认
- 步骤 :
- 客户端调用
close()
方法,结束文件写入。 - NameNode 执行以下操作:
- 更新元数据:标记文件为"已关闭"。
- 写入 EditLog:记录"关闭文件"操作。
- 客户端收到最终确认,写入流程完成。
- 客户端调用
5.2 EditLog 的核心机制
- EditLog 的作用 :
- 记录所有元数据变更操作(如创建文件、分配块、删除文件等)。
- 用于故障恢复:NameNode 重启时,通过加载
FsImage
(元数据快照)并重放 EditLog,恢复最新状态。
- EditLog 写入流程 :
- 同步写入 :每个元数据变更操作必须先写入 EditLog 磁盘,再更新内存中的元数据。
- 高可用(HA)场景 :
- 在 HA 架构中,Active NameNode 将 EditLog 写入 JournalNode 集群 (至少 3 节点),通过 Quorum 机制保证一致性。
- Standby NameNode 从 JournalNode 读取 EditLog,实时同步元数据。
- EditLog 与 FsImage 的合并 :
- Secondary NameNode (或 HA 中的 Standby NameNode)定期合并
FsImage
和EditLog
,生成新的FsImage
,减少 NameNode 重启时间。
- Secondary NameNode (或 HA 中的 Standby NameNode)定期合并
5.3 写入流程的关键设计思想
- 元数据与数据分离 :
- NameNode 仅管理元数据,DataNode 处理实际数据存储,避免单点瓶颈。
- 流水线复制 :
- 数据块并行传输到多个 DataNode,提升写入效率。
- Write-Ahead Logging (WAL) :
- 通过 EditLog 的持久化,确保元数据变更的原子性和持久性。
5.4 故障处理示例
- DataNode 宕机 :
- 客户端写入时,若某个 DataNode 失败,管道会自动切换到剩余节点,并通知 NameNode 补充副本。
- NameNode 宕机 :
- 在 HA 架构中,Standby NameNode 通过 ZooKeeper 接管服务,继续处理写入请求。
6、HDFS 读数据
HDFS 的客户端读取流程是一个高效、分布式的过程,核心目标是 就近读取数据 并确保高吞吐量。
6.1 读数据流程
1. 客户端发起读取请求
- 步骤 :
- 客户端调用
open()
方法向 NameNode 发起 读取文件请求。 - NameNode 执行以下操作:
- 检查权限:验证客户端是否有读取权限。
- 获取元数据:从内存中检索文件的元数据(文件名、块列表及每个块的 DataNode 位置列表)。
- 响应客户端:返回文件的所有块信息及每个块的 DataNode 地址列表(按网络拓扑排序)。
- 客户端调用
- 关键点 :
- NameNode 不传输数据:仅返回元数据,避免成为性能瓶颈。
- 块位置排序 :DataNode 列表按与客户端的网络距离(如相同节点 → 同机架 → 跨机架)排序,优先选择最近的节点。
2. 客户端建立与 DataNode 的连接
- 步骤 :
- 客户端根据块列表中的 DataNode 地址,选择 最近的 DataNode(如客户端所在节点或同机架节点)建立连接。
- 客户端向选中的 DataNode 发送读取请求,指定块 ID 和偏移量(offset)。
- 关键点 :
- 并行读取:若文件由多个块组成,客户端可并行连接多个 DataNode 读取不同块。
- 数据本地性优化 :优先从本地或近端 DataNode 读取,减少网络开销。
3. DataNode 传输数据
- 步骤 :
- DataNode 从本地磁盘读取数据块(如
blk_1234
),按数据包(默认 64KB)流式传输给客户端。 - 客户端接收数据包后,按顺序组装成完整的数据块,并缓存在本地内存中。
- 客户端重复请求下一个块的 DataNode 列表(若文件包含多个块),直至读取完成。
- DataNode 从本地磁盘读取数据块(如
- 关键点 :
- 流式传输:数据以流水线形式传输,客户端无需等待整个块下载完毕即可开始处理。
- 数据完整性校验 :客户端通过校验和(Checksum)验证接收到的数据是否损坏。若发现损坏,从其他副本重新读取。
4. 容错处理(DataNode 故障)
- 场景 :
- 客户端尝试连接某个 DataNode 时失败(如超时或校验和不匹配)。
- 处理流程 :
- 客户端标记该 DataNode 为不可用(临时状态)。
- 客户端向 NameNode 报告故障,并重新请求该块的副本位置。
- NameNode 返回其他副本的 DataNode 地址列表(排除故障节点)。
- 客户端切换到新的 DataNode 继续读取。
5. 关闭读取连接
- 步骤 :
- 客户端完成所有数据块的读取后,调用
close()
方法释放资源。 - 客户端本地缓存的数据可进一步处理(如 MapReduce 计算、分析等)。
- 客户端完成所有数据块的读取后,调用
6.3 关键设计思想
- 数据本地性(Data Locality) :
- 优先从距离客户端最近的 DataNode 读取数据,减少网络传输开销。
- 支持计算框架(如 MapReduce、Spark)将任务调度到数据所在节点(移动计算而非移动数据)。
- 流式读取(Streaming Read) :
- 客户端按需读取数据块,无需预先加载整个文件,适合处理超大规模数据集。
- 校验和机制(Checksum) :
- 每个数据块存储时生成校验和,客户端读取时验证数据完整性。
- 若数据损坏,客户端自动从其他副本恢复。
6.3 读取流程与写入流程的对比
特性 | 写入流程 | 读取流程 |
---|---|---|
NameNode 角色 | 管理元数据变更(写 EditLog) | 仅提供元数据查询(无 EditLog 写入) |
DataNode 交互 | 通过流水线复制到多个节点(写多副本) | 直接连接最近的节点(单副本读取) |
数据传输方向 | 客户端 → DataNode(推送数据) | DataNode → 客户端(拉取数据) |
容错机制 | 自动切换 DataNode 并补充副本 | 自动切换到其他副本 DataNode |
性能优化 | 高吞吐量(牺牲延迟) | 低延迟(就近读取) |
7、HDFS 高可用(HA)
-
Active/Standby NameNode:
- 通过 ZooKeeper 实现故障自动切换。
- 使用 JournalNode 集群共享 EditLog,确保元数据一致性。
-
DataNode:同时向所有 NameNode 汇报块信息。
-
配置示例(hdfs-site.xml)
xml<!-- 启用 HA 并定义集群名称 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> </property> <!-- 指定主备 NameNode 标识 --> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property> <!-- 配置主备节点的 RPC 和 HTTP 地址 --> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>active-nn:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>active-nn:9870</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>standby-nn:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>standby-nn:9870</value> </property> <!-- 指定 JournalNode 集群地址 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://jn1:8485;jn2:8485;jn3:8485/mycluster</value> </property> <!-- 启用 ZooKeeper 自动故障转移 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property>
8、优缺点
(1)优点
- 海量存储:支持 PB 级数据扩展。
- 高容错:自动处理节点故障,数据副本保障可靠性。
- 高吞吐:适合批量数据处理(如 MapReduce、Spark)。
- 成本低:可部署在廉价商用硬件上。
(2)缺点 - 不适合低延迟场景:如实时查询(HBase 更合适)。
- 小文件问题:大量小文件会占用 NameNode 内存(需合并或使用 HAR 归档)。
- 单 Namespace:单个 NameNode 管理所有元数据,规模过大时可能成为瓶颈(可通过 Federation 分片解决)。
(3)小文件优化策略 - 合并小文件:使用Hadoop Archive (HAR)将多个小文件打包为归档文件,减少元数据开销。
- SequenceFile:将小文件存储为键值对形式的SequenceFile,合并后作为大文件处理。
- 调整块大小:对特定目录设置更小的块大小(需权衡元数据管理效率)。
9、应用场景
- 大数据分析:存储原始数据供 MapReduce、Spark 等计算框架处理。
- 数据仓库:作为 Hive、HBase 的底层存储。
- 日志聚合:集中存储分布式系统的日志(如 Kafka + HDFS)。
- 冷数据备份:长期存储历史数据(成本低于传统存储)。
三、Yarn
1、YARN 是什么?
Hadoop YARN(Yet Another Resource Negotiator) 是 Hadoop 2.x 及更高版本的核心资源管理和作业调度框架。它的核心目标是解耦资源管理与计算逻辑,使 Hadoop 从单一的 MapReduce 计算模式扩展为支持多种计算框架(如 Spark、Flink、Hive)的统一平台。
2、YARN 的诞生背景
- Hadoop 1.x 的局限性
- 紧耦合架构:MapReduce 同时负责资源管理和任务计算,难以支持其他计算模型。
- 扩展性差:JobTracker 单点瓶颈,集群规模受限(通常不超过 4000 节点)。
- 资源利用率低:静态槽位(Slot)分配导致 CPU 和内存资源浪费。
- YARN 的改进
- 资源与计算分离:YARN 专注资源管理,计算框架(如 MapReduce、Spark)作为应用运行其上。
- 高扩展性:支持万级节点集群。
- 多租户多框架:允许同时运行多种计算模型(批处理、流处理、交互式查询)。
3、YARN 的核心设计目标
- 资源统一管理:将 CPU、内存、磁盘等资源统一抽象为可分配的容器(Container)。
- 高扩展性:支持数千节点的超大规模集群。
- 多计算框架支持:允许多种计算模型(批处理、流处理、交互式查询)共享集群资源。
- 高容错性:自动处理节点或任务故障。
- 资源隔离:通过容器化技术保障不同任务的资源独立性
4、YARN 架构
YARN 采用 主从架构 ,核心组件包括:
1. ResourceManager(RM,主节点)
- 角色:全局资源调度器,管理整个集群的资源。
- 子组件 :
- Scheduler(调度器) :
- 纯调度器,仅负责资源分配(不关注任务状态)。
- 支持多种调度策略:FIFO、Capacity Scheduler(默认)、Fair Scheduler。
- ApplicationsManager(应用管理器) :
- 接收客户端提交的应用(如 MapReduce、Spark 作业)。
- 启动应用的 ApplicationMaster(AM),并监控其生命周期。
- Scheduler(调度器) :
- 高可用(HA) :支持 Active/Standby RM,通过 ZooKeeper 实现故障切换。
2. NodeManager(NM,从节点) - 角色:管理单个节点的资源,执行具体任务。
- 职责 :
- 向 RM 注册并周期性汇报资源使用情况(CPU、内存等)。
- 根据 RM 指令启动或停止容器(Container)。
- 监控容器资源使用,防止资源超限(如内存溢出)。
3. ApplicationMaster(AM,应用级主节点)
- 角色:每个应用(如一个 MapReduce 作业)对应一个 AM,负责任务协调。
- 职责 :
- 向 RM 申请资源(容器),并与 NM 通信启动任务。
- 监控任务执行状态,处理失败任务的重试。
- 向 RM 汇报应用进度和最终状态。
- 关键特性 :
- AM 本身运行在容器中,由 RM 调度启动。
- 不同计算框架(如 Spark、Flink)需实现自己的 AM。
4. Container(容器)
- 角色:资源分配的基本单位,封装了 CPU、内存等资源。
- 特性 :
- 由 RM 分配给 AM,由 NM 在节点上启动。
- 运行具体任务(如 Map Task、Reduce Task 或 Spark Executor)。
5、YARN 工作流程
提交一个 MapReduce 作业的完整流程示意图:
客户端 → RM(提交应用) → RM 启动 AM → AM 注册到 RM
AM → RM(申请资源) → RM 分配容器 → AM → NM(启动容器)
NM → AM(任务状态) → AM → RM(应用进度)
任务完成 → AM 释放容器 → RM 回收资源
1. 客户端提交应用
- 动作 :
- 用户通过
yarn jar
或 API 提交应用(如 MapReduce 作业)。 - 客户端将应用的 JAR 包、配置文件等上传至 HDFS 临时目录 (如
/user/<user>/.staging/<application_id>/
)。
- 用户通过
- 通信 :
- 客户端与 RM 通信,发送应用提交请求。
2. RM 分配 AM 容器
- 客户端与 RM 通信,发送应用提交请求。
- RM 的决策逻辑 :
- 资源请求 :
- RM 的
ApplicationsManager
收到提交请求后,生成唯一的 Application ID ,生成一个 AM 容器资源请求。 - 资源规格:默认 1.5GB 内存 + 1 CPU 核(可配置)。
- RM 的
- 调度器选择 NM :
- RM 的
Scheduler
根据调度策略(如 Capacity Scheduler)选择目标 NM。 - 选择依据:节点资源余量、健康状态、数据本地性(若应用有输入数据)。
- RM 的
- 分配容器 :
- RM 的
Scheduler
生成一个 Container ID,标记该容器用于启动 AM。 - 向选中的 NM 发送 启动 AM 容器的指令(通过 RPC)。
- RM 的
- 资源请求 :
- 关键点 :
- RM 不直接启动 AM,而是扮演"调度指挥官"角色。
- AM 容器是普通容器 :与其他任务容器无本质区别,但运行的是 AM 的主类(如
MRAppMaster
)。
3. NM 启动 AM 容器
- NM 的执行步骤 :
- 接收指令 :NM 收到 RM 的
StartContainerRequest
。 - 资源本地化 :
- 从 HDFS 下载 AM 的依赖文件(JAR 包、配置文件)到 本地工作目录。
- 示例路径:
/tmp/hadoop-yarn/nm-local-dir/usercache/<user>/appcache/<application_id>/container_<id>/
。
- 启动进程 :
- 在隔离的容器环境中启动 JVM,执行 AM 主类(如
org.apache.hadoop.mapreduce.v2.app.MRAppMaster
)。
- 在隔离的容器环境中启动 JVM,执行 AM 主类(如
- 资源监控 :
- NM 监控容器的 CPU、内存使用,确保不超限。
4. AM 注册与资源申请
- NM 监控容器的 CPU、内存使用,确保不超限。
- 接收指令 :NM 收到 RM 的
- AM 向 RM 注册 :
- AM 启动后,立即向 RM 发送
RegisterApplicationMasterRequest
,包含:- 主机名(AM 所在 NM 的地址)。
- RPC 端口(用于后续任务状态汇报)。
- AM 启动后,立即向 RM 发送
- AM 申请任务容器 :
- AM 根据应用需求(如 MapReduce 的 Map Task 数量,一个Task便是一个独立的容器),构造
ResourceRequest
列表。 - 请求内容:
- 资源量:每个容器需要的 CPU 核数、内存(如 1 核、2GB)。
- 数据本地性(Locality):优先请求存储输入数据的节点(HDFS 数据块所在节点)。
- 优先级:任务优先级(如 Map Task 优先级高于 Reduce Task)。
- 通过心跳机制(默认 1 秒间隔)向 RM 的
Scheduler
发送请求。
5. RM 分配任务容器
- AM 根据应用需求(如 MapReduce 的 Map Task 数量,一个Task便是一个独立的容器),构造
- 调度器逻辑 :
- 匹配资源请求:根据队列配额、节点负载、数据本地性优先级分配容器。
- 返回分配结果 :通过心跳响应返回一组
Container
对象,包含 NM 地址和资源详情。
- AM 的后续动作 :
- 向 NM 发送
StartContainerRequest
启动任务(如 Map Task)。
6. AM 与 NM 启动容器
- 向 NM 发送
- AM 通知 NM 启动容器 :
- AM 收到容器分配后,向对应的 NM 发送 容器启动命令(ContainerLaunchContext)。
- 启动命令内容 :
- 任务执行的命令行(如
java -jar map_task.jar
)。 - 依赖的 JAR 包、配置文件(从 HDFS 下载的路径)。
- 环境变量、安全令牌(用于访问 HDFS 或其他服务)。
- 任务执行的命令行(如
- NM 启动容器 :
- NM 根据指令创建容器进程(如启动 JVM 执行 Map Task)。
- 资源隔离 :
- 使用 Linux CGroup 限制容器的 CPU 和内存使用。
- 监控容器资源使用,防止超限(如内存溢出时强制终止容器)。
- 任务执行与状态汇报 :
- 容器内任务执行期间,AM 持续接收 NM 的 容器状态更新。
- AM 向 RM 汇报应用进度(如 Map Task 完成 50%)。
7. 动态资源申请与释放
- 增量资源申请 :
- 若应用需要更多资源(如 Reduce Task 阶段),AM 可发送新的资源请求。
- RM 根据当前集群负载动态分配额外容器。
- 资源释放 :
- 当任务完成(如 Map Task 执行完毕),AM 通知 NM 释放容器。
- RM 将释放的资源重新加入调度池,供其他应用使用。
8. 容错与重试机制
- 容器故障 :
- NM 检测容器异常(如进程崩溃),通知 AM 和 RM。
- AM 重新申请资源,RM 分配新容器,任务重新执行。
- AM 故障 :
- RM 检测 AM 心跳丢失,触发 AM 重启。
- 重启的 AM 从 RM 获取应用元数据,恢复任务进度。
- NM 故障 :
- RM 检测 NM 心跳丢失,标记该节点为失效。
- AM 重新申请资源,避开故障节点。
6、YARN 关键特性
1. 资源调度模型
- 层级调度 :
- 一级调度:RM 将资源分配给 AM。
- 二级调度:AM 将资源分配给具体任务。
- 资源粒度 :支持动态调整容器资源(如 CPU 核数、内存大小)。
2. 多租户支持 - 队列管理 :
- 通过队列(Queue)划分资源,不同团队或应用共享集群。
- 例如:设置
dev
、prod
队列,分配不同资源配额。
3. 容错机制
- AM 容错:若 AM 失败,RM 会重启 AM 并恢复任务状态。
- NM 容错 :若 NM 失联,RM 将其标记为失效,重新分配任务到其他节点。
4. 资源隔离 - 基于 Linux CGroup:实现 CPU、内存隔离。
- 磁盘隔离:限制任务对本地磁盘的使用。
7、YARN 高可用(HA)
- ResourceManager HA:
- 主备 RM 通过 ZooKeeper 实现自动故障转移。
- 状态存储:RM 状态持久化到 HDFS 或 ZooKeeper。
- NodeManager 重连:NM 自动检测 RM 故障并切换到新 RM。
8、YARN 配置与调优
(1)关键配置参数
-
资源分配:
xml<!-- 单个 Container 最小/最大内存 --> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>1024</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>8192</value> </property>
-
堆内存设置:
xml<!-- NodeManager 堆内存 --> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>16384</value> <!-- 根据物理内存调整 --> </property>
(2)调优建议
- 避免资源超售:确保
yarn.nodemanager.resource.memory-mb
不超过节点物理内存。 - 启用日志聚合:集中存储任务日志,便于排查问题。
- 监控工具:使用 YARN Timeline Server 或第三方工具(如 Cloudera Manager)监控资源使用。
9、YARN 生态系统
YARN 支持多种计算框架运行,形成统一资源池:
- 批处理:MapReduce、Hive on Tez。
- 实时计算:Apache Spark、Apache Flink。
- 交互式查询:Apache Hive LLAP、Presto。
- 机器学习:TensorFlow on YARN、Apache Mahout。
10、YARN 的优缺点
(1)优点
- 资源利用率高:动态分配,避免 Slot 静态划分的浪费。
- 多框架支持:打破 MapReduce 的垄断,生态丰富。
- 扩展性强:轻松支持大规模集群。
(2)缺点 - 调度延迟:相比 Mesos 或 Kubernetes,实时任务调度效率较低。
- 配置复杂:需根据业务负载调整调度策略和资源参数。
11、YARN vs. Kubernetes
特性 | YARN | Kubernetes |
---|---|---|
定位 | 大数据生态资源调度 | 通用容器编排 |
资源模型 | 基于内存/CPU | 基于容器(支持 GPU、存储卷) |
调度粒度 | 应用级(如 MapReduce 作业) | 容器/Pod 级 |
适用场景 | Hadoop 生态批处理任务 | 微服务、云原生应用 |
社区生态 | 与 Hadoop 工具深度集成 | 广泛的云原生工具支持 |
四、MapReduce
1、Map Reduce是什么?
MapReduce 是一种分布式计算编程模型,用于高效处理大规模数据集(TB/PB 级)。它将计算任务分为两个阶段:
- Map 阶段 :并行处理输入数据,生成中间键值对(
<key, value>
)。 - Reduce 阶段 :对中间结果按
key
聚合,生成最终输出。
核心思想: - 分而治之:将大数据集切分为小数据块(InputSplit),并行处理。
- 移动计算而非数据 :将计算逻辑(Map/Reduce 代码)分发到数据所在的节点执行,减少网络传输。
典型流程
java
**Input** → 2. **Map** → 3. **Shuffle & Sort** → 4. **Reduce** → 5. **Output**
核心设计思想:
- 简单编程模型:用户只需实现 map() 和 reduce() 函数,无需关注分布式细节(如任务调度、容错)。
- 横向扩展(Scale-out):通过增加廉价节点扩展计算能力,处理 PB 级数据。
- 数据本地化(Data Locality):尽可能将计算任务调度到存储数据的节点,减少网络传输。
- 容错机制:自动重试失败的任务,保障作业完成。
2、MapReduce 工作流程
整体流程图
java
输入数据 → Input Split → Map Task → Combine(可选) → Partition & Sort → Shuffle → Reduce Task → 输出结果
(1) Input Split(输入分片)
-
目的:将输入数据(如 HDFS 文件)切分为多个逻辑分片(Split),每个分片对应一个 Map Task。
-
规则 :
- 默认分片大小等于 HDFS 块大小(128MB 或 256MB)。
- 分片是逻辑划分,不实际切割文件(例如一个 1GB 文件会被分为 8 个分片)。
-
示例 :
java// 输入文件:hdfs:///data/logs.txt(1GB,8 个 HDFS 块) // 生成 8 个 InputSplit,启动 8 个 Map Task。
(2) Map 阶段
-
Map Task :
- 每个 Map Task 处理一个 Input Split。
- 输入格式:
<Key, Value>
对(如文件偏移量为 Key,一行文本为 Value)。 - 输出格式:用户定义的中间结果(如
<Word, 1>
)。
-
并行度 :由 Input Split 数量决定(如 8 个分片 → 8 个 Map Task)。
(3) Combine(可选) -
作用:在 Map 端本地聚合中间结果,减少 Shuffle 数据传输量。
-
本质:是一个本地化的 Reduce 操作。
-
示例 :
java// Map 输出:<"hadoop", 1>, <"hadoop", 1> // Combine 后:<"hadoop", 2>
(4) Partition & Sort
- Partition(分区) :
- 将 Map 输出的 Key 按哈希值分配到不同 Reduce Task。
- 默认分区数等于 Reduce Task 数量(由
job.setNumReduceTasks()
设置)。
- Sort(排序) :
- Map 输出在写入磁盘前按键排序(默认升序)。
- 排序后的数据按分区存储(如
part-r-00000
、part-r-00001
)。
(5) Shuffle(混洗)
- 过程 :
- Map Task 将排序后的数据按分区写入磁盘。
- Reduce Task 从所有 Map Task 的对应分区 拉取数据。
- 数据通过网络传输到 Reduce 节点。
- 优化 :
- 压缩:可启用中间数据压缩(如 Snappy)减少网络流量。
- 合并 :Reduce 端合并来自多个 Map 的同一分区数据。
(6) Reduce 阶段
- Reduce Task :
- 输入:同一分区的所有 Key 及其 Value 列表(如
<"hadoop", [1, 1, 1]>
)。 - 输出:最终结果(如
<"hadoop", 3>
)。
- 输入:同一分区的所有 Key 及其 Value 列表(如
- 并行度 :由用户指定的 Reduce Task 数量决定(默认 1)。
(7) 输出写入 - Reduce 结果写入 HDFS(或其他存储系统),每个 Reduce Task 生成一个输出文件(如
part-r-00000
)。
3、Shuffle
3.1 Shuffle 的定义
Shuffle的本意是洗牌、混洗,指的是把一组有规则的数据尽量打乱成无规则的数据。而在MapReduce中,Shuffle更像是洗牌的逆过程,指的是将Map端的无规则输出按指定的规则"打乱"成具有一定规则的数据,以便Reduce端接收处理。
3.2 Shuffle的作用是什么?
- 数据分发:Map任务输出的键值对需要根据键(Key)分发到对应的Reduce任务中。例如,统计词频时,所有相同的单词需要发送到同一个Reduce任务。
- 数据排序与分组 :在Reduce处理前,相同Key的键值对必须被分组(Group)并排序(Sort),例如
(apple, 1)
和(apple, 1)
需要合并为(apple, [1, 1])
。 - 网络传输优化:高效地将Map输出的数据从Map节点传输到Reduce节点,减少网络开销。
3.3 Shuffle的具体流程
Shuffle分为Map端Shuffle 和Reduce端Shuffle ,具体步骤如下:
Map端Shuffle
- 分区(Partitioning)
Map输出的每个键值对会根据Key的哈希值分配到不同的分区(Partition),每个分区对应一个Reduce任务。例如,若有3个Reduce任务,则Map输出分为3个分区。 - 排序(Sorting)
每个分区内的数据按键(Key)排序,为后续合并和Reduce处理做准备。 - 溢出(Spill)与合并(Combine)
- Map输出会先写入内存缓冲区,缓冲区满后**溢出(Spill)**到磁盘,形成多个临时文件。
- 溢出过程中可能执行Combiner (本地Reduce),例如将
(apple, [1, 1])
合并为(apple, 2)
,减少数据量。
- 归并(Merge)
所有溢出文件最终合并为一个已分区且排序 的大文件,等待Reduce任务拉取。
Reduce端Shuffle - 数据拉取(Fetch)
Reduce任务从所有Map任务的输出中拉取属于自己的分区数据(通过HTTP请求)。 - 归并排序(Merge Sort)
Reduce将来自不同Map的相同分区的数据进行多路归并排序,最终形成一个全局有序的数据集。 - 分组(Grouping)
将相同Key的键值对合并为(Key, [Value1, Value2, ...])
,供Reduce函数处理。
4、MapReduce提交到Yarn
不用自己编写MapReduce代码,Hadoop官方有提供示例代码:
shell
$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.0.jar
可通过 hadoop jar来提交MapReduce到yarn上运行,语法:
xml
hadoop jar 程序文件 java类名 程序参数...
调用示例:
xml
hadoop fs -mkdir -p /input
hadoop fs -mkdir -p /out
hadoop fs put word.txt /input/
hadoop jar /app/hadoop-3.4.0/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.0.jar wordcount hdfs://node1:9870/input hdfs://node1:9870/output/wc
5、性能调优建议
(1) 控制 Map Task 数量
-
增大分片大小 :合并小文件,减少 Map Task 数量。
// 设置最小分片大小(合并小文件) FileInputFormat.setMinInputSplitSize(job, 256 * 1024 * 1024); // 256MB
-
减少分片大小 :处理非可切分文件(如 GZIP 压缩文件)时,避免单个 Map Task 处理过大文件。
(2) 优化 Reduce Task 数量 -
经验公式 :
Reduce Task 数量 ≈ 集群可用 Reduce 容器槽位数 × 1.5
。 -
数据倾斜处理 :若某些 Key 数据量过大,需增加 Reduce Task 数量并优化分区策略。
(3) 资源参数调优
| 参数 | 说明 | 推荐值(示例) |
| :------------------------------------- | :--------------------- | :-------------------------- |
|mapreduce.map.memory.mb
| Map 容器内存 | 根据任务复杂度调整(2-4GB) |
|mapreduce.reduce.memory.mb
| Reduce 容器内存 | 通常为 Map 的 2 倍(4-8GB) |
|yarn.scheduler.maximum-allocation-mb
| 集群单容器最大内存限制 | 必须 ≥ Map/Reduce 容器内存 |
(4)使用 Combiner
减少 Map 到 Reduce 的数据传输量。
(5)压缩中间数据
启用 Snappy/LZO 压缩,降低磁盘和网络开销。
(6)避免数据倾斜
自定义分区策略,均匀分配 Key 到 Reducer。
6、MapReduce 优缺点
(1)优点
- 高容错性:任务失败自动重试,数据多副本保障可靠性。
- 线性扩展:增加节点即可提升处理能力。
- 适合批处理:吞吐量高,适合离线大数据分析。
(2)缺点 - 高延迟:不适合实时或迭代计算(Spark 更优)。
- 磁盘 I/O 瓶颈:Map 和 Reduce 间需多次读写磁盘(Spark 利用内存优化此问题)。
- 编程复杂度:复杂逻辑需组合多个 MapReduce 作业(如链式处理)。
7、应用场景
- 日志分析:分析 Web 服务器日志,统计 PV/UV、错误率。
- 数据清洗:过滤无效记录,转换数据格式。
- 搜索引擎:构建倒排索引(如 PageRank 计算)。
- 机器学习:分布式训练简单模型(如朴素贝叶斯)。
8、与 Spark 对比
特性 | MapReduce | Spark |
---|---|---|
计算模型 | 基于磁盘的批处理 | 基于内存的批处理/流处理 |
延迟 | 高(分钟级) | 低(秒级) |
API 易用性 | 较低(需手动组合作业) | 高(支持 SQL、流式、图计算) |
容错机制 | 任务重试 | RDD 血统(Lineage)重建 |
适用场景 | 离线大数据处理 | 实时分析、迭代计算(如 ML) |
9、总结
MapReduce 是 Hadoop 生态的基石,尽管其性能已被 Spark 等新一代框架超越,但其设计思想(如分片、Shuffle)仍深刻影响分布式计算领域。学习 MapReduce 的意义在于:
- 理解分布式计算的底层逻辑。
- 掌握大数据批处理的经典范式。
- 为优化 Spark/Flink 等框架提供理论基础。
实际生产中,建议根据场景选择合适的计算引擎(如离线用 Hive on MapReduce,实时用 Spark/Flink)。
五、Hadoop 集群部署
Hadoop部署有三种模式:单机模式、伪分布式模式和完全分布式模式。
此外,Hadoop的部署还可以根据是否具备高可用性(HA)特性来进行区分。
1、环境准备
主机IP | 主机名 | 服务 |
---|---|---|
10.211.10.1 | rl-node1 | NameNode、DataNode、SecondaryNameNode ResourceManager、NodeManager、WebAppProxyServer、JobHistoryServer |
10.211.10.2 | rl-node2 | DataNode、NodeManager |
10.211.10.3 | rl-node3 | DataNode、NodeManager |
在每台机器上,用 root 用户执行以下脚本: |
bash
# 设置主机名(注意,每个机器主机名是不一样的)
hostnamectl set-hostname rl-node1
bash
# 设置本机域名解析(可用 ip a 查看虚拟机IP)
tee -a /etc/hosts << EOF
10.211.10.1 rl-node1
10.211.10.2 rl-node2
10.211.10.3 rl-node3
EOF
# 关闭防火墙
systemctl stop firewalld
# 设置SSH免密
ssh-keygen -t rsa -b 4096 -P "" -f ~/.ssh/id_rsa -C "zhangsan@example.com"
ssh-copy-id hadoop@10.211.10.1
ssh-copy-id hadoop@10.211.10.2
ssh-copy-id hadoop@10.211.10.3
# 集群时间同步(虚拟机可跳过这一步)
dnf install -y chrony
systemctl start chronyd
systemctl enable chronyd
# 创建app目录用来存放应用
mkdir /app
chmod 777 /app
# 安装JDK
curl -O https://download.oracle.com/otn/java/jdk/8u431-b10/0d8f12bc927a4e2c9f8568ca567db4ee/jdk-8u431-linux-aarch64.tar.gz
tar -zxvf jdk-8u341-linux-x64.tar.gz -C /app
tee -a /etc/profile <<'EOF'
export JAVA_HOME=/app/jdk1.8.0_341
export CLASSPATH=$JAVA_HOME/lib/
export PATH=$PATH:$JAVA_HOME/bin
EOF
source /etc/profile
# 创建 Hadoop 用户
useradd hadoop
sudo usermod -aG sudo hadoop
su - hadoop
2、分布式部署
主机IP | 主机名 | 服务 |
---|---|---|
10.211.10.1 | rl-node1 | DataNode、NameNode、SecondaryNameNode NodeManager、ResourceManager、WebAppProxyServer、JobHistoryServer |
10.211.10.2 | rl-node2 | DataNode、NodeManager |
10.211.10.3 | rl-node3 | DataNode、NodeManager |
在 rl-node1 上执行以下命令: |
bash
# 下载
wget https://dlcdn.apache.org/hadoop/common/hadoop-3.4.0/hadoop-3.4.0-aarch64.tar.gz
# 解压
tar -zxvf https://dlcdn.apache.org/hadoop/common/hadoop-3.4.0/hadoop-3.4.0-aarch64.tar.gz
# 配置环境变量
tee -a ~/.bash_profile << 'EOF'
export HADOOP_HOME=/app/hadoop-3.4.0
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
EOF
source ~/.bash_profile
# 修改配置文件
printf "rl-node1\nrl-node2\nrl-node3" > hadoop-3.4.0/etc/hadoop/workers
tee -a hadoop-3.4.0/etc/hadoop/hadoop-env.sh << 'EOF'
export JAVA_HOME=/app/jdk1.8.0_431
export HADOOP_HOME=/app/hadoop-3.4.0
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export HADOOP_LOG_DIR=$HADOOP_HOME/logs
EOF
sed -i -e '/<configuration>/a\
<property>
<name>fs.defaultFS</name>
<value>hdfs://rl-node1:8020</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>131072</value>
</property>' hadoop-3.4.0/etc/hadoop/core-site.xml
sed -i -e '/<configuration>/a\
<property>
<name>dfs.datanode.data.dir.perm</name>
<value>700</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/app/hadoop-3.4.0/data/dn</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/app/hadoop-3.4.0/data/nn</value>
</property>
<property>
<name>dfs.namenode.name.hosts</name>
<value>rl-node1,rl-node2,rl-node3</value>
</property>
<property>
<name>dfs.namenode.handler.count</name>
<value>100</value>
</property>
<property>
<name>dfs.blocks</name>
<value>268435456</value>
</property>
' hadoop-3.4.0/etc/hadoop/hdfs-site.xml
tee -a hadoop-3.4.0/etc/hadoop/mapred-env.sh << 'EOF'
export JAVA_HOME=/app/jdk1.8.0_431
export HADOOP_JOB_HISTORYSERVER_HEAPSIZE=1000
export HADOOP_MAPRED_ROOT_LOGGER=INFO,RFA
EOF
sed -i -e '/<configuration>/a\
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>node1:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>node1:19888</value>
</property>
<property>
<name>mapreduce.jobhistory.intermediate-done-dir</name>
<value>/data/mr-history/tmp</value>
</property>
<property>
<name>mapreduce.jobhistory.done-dir</name>
<value>/data/mr-history/done</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
' hadoop-3.4.0/etc/hadoop/mapred-site.xml
tee -a hadoop-3.4.0/etc/hadoop/yarn-env.sh << 'EOF'
export JAVA_HOME=/app/jdk1.8.0_431
export HADOOP_HOME=/app/hadoop-3.4.0
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export HADOOP_LOG_DIR=$HADOOP_HOME/logs
EOF
sed -i -e '/<configuration>/a\
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.log.server.url</name>
<value>http://node1:19888/jobhistory/logs</value>
</property>
<property>
<name>yarn.web-proxy.address</name>
<value>node1:8089</value>
<description>proxy server hostname and port</description>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
<description>Configuration to enable or disable log aggregation</description>
</property>
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>/tmp/logs</value>
<description>Configuration to enable or disable log aggregation</description>
</property>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>node1</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
</property>
<property>
<name>yarn.nodemanager.local-dirs</name>
<value>/app/hadoop-3.4.0/data/nm-local</value>
<description>Comma-separated list of paths on the local filesystem where intermediate data is written.</description>
</property>
<property>
<name>yarn.nodemanager.log-dirs</name>
<value>/app/hadoop-3.4.0/data/nm-log</value>
<description>Comma-separated list of paths on the local filesystem where logs are written.</description>
</property>
<property>
<name>yarn.nodemanager.log.retain-seconds</name>
<value>10800</value>
<description>Default time (in seconds) to retain log files on the NodeManager Only applicable if log-aggregation is disabled.</description>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
<description>Shuffle service that needs to be set for Map Reduce applications.</description>
</property>
' hadoop-3.4.0/etc/hadoop/yarn-site.xml
#将 `hadoop-3.4.0` 拷贝到其他两个机器上
scp -r /app/hadoop-3.4.0 hadoop@10.211.10.2:/app
scp -r /app/hadoop-3.4.0 hadoop@10.211.10.3:/app
# 切换用户,添加Hadoop环境变量
ssh 10.211.10.2
tee -a ~/.bash_profile << 'EOF'
export HADOOP_HOME=/app/hadoop-3.4.0
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
EOF
source ~/.bash_profile
exit
ssh 10.211.10.3
tee -a ~/.bash_profile << 'EOF'
export HADOOP_HOME=/app/hadoop-3.4.0
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
EOF
source ~/.bash_profile
exit
# 初始化namenode
hadoop namenode -format
# 启动 HDFS:http://node1:9870
start-dfs.sh
# 启动 yarn:http://node1:8088
start-yarn.sh
# 启动 历史服务器
mapred --daemon start historyserver
关闭服务:
shell
# 关闭 HDFS
stop-dfs.sh
# 关闭 yarn
stop-yarn.sh
# 关闭历史服务器
mapred --daemon stop historyserver
3、客户端工具
- 在
jetbrains
产品(idea、pycharm...) 中使用插件big data tools
- 通过NFS在windows挂载HDFS文件系统
4、Hadoop目录文件说明
- bin,存放Hadoop的各类程序(命令
- etc,存放Hadoop的配置文件
- include,c语言的一些头文件
- lib,存放Linux系统的动态链接库(.so文件)
- libexec,存放配置Hadoop系统的脚本文件(.sh和.cmd)
- licenses-binary,存放许可证文件
- sbin,t管理员程序(super bin)
- share,存放二进制源码(Java jar包)
5、配置文件
hadoop-3.4.0/etc/hadoop 目录下配置文件说明:
文件 | 核心作用 | 典型配置项示例 |
---|---|---|
core-site.xml | 全局文件系统和基础配置 | fs.defaultFS, hadoop.tmp.dir |
hdfs-site.xml | HDFS存储和节点配置 | dfs.replication, dfs.namenode.name.dir |
mapred-site.xml | MapReduce作业调度配置 | mapreduce.framework.name |
yarn-site.xml | YARN资源管理和任务调度 | yarn.resourcemanager.hostname |
workers | 定义DataNode和NodeManager节点 | 节点主机名列表 |
hadoop-env.sh | 设置Java环境及内存参数 | JAVA_HOME, HADOOP_HEAPSIZE |
**-default.xml | 默认配置文件,不必修改 |
六、分布式 HA方案
1、 高可用(HA)的背景知识
(1)单点故障
单点故障(英语:single point of failure,缩写 SPOF)是指系统中某一点一旦失效,就会让整个系统无法运作,换句话说,单点故障即会整体故障。
(2)主备集群
解决单点故障,实现系统服务高可用的核心并不是让故障永不发生,而是让故障的发生对业务的影响降到最小。因为软硬件故障是难以避免的问题。
当下企业中成熟的做法就是给单点故障设置备份,形成主备架构。通俗描述就是当主挂掉,备份顶上,短暂的中断之后继续提供服务。
常见的是一主一备架构,当然也可以一主多备。备份越多,容错能力越强,与此同时,冗余也越大,浪费资源。
(3)Active、Standby
- Active:主角色。活跃的角色,代表正在对外提供服务的角色服务。任意时间有且只有一个 active 对外提供服务。
- Standby:备份角色。需要和主角色保持数据、状态同步,并且时刻准备切换成主角色(当主角色挂掉或者出现故障时),对外提供服务,保持服务的可用性。
(4)高可用
高可用性(英语:high availability,缩写为 HA),IT术语,指系统无中断地执行其功能的能力,代表系统的可用性程度。是进行系统设计时的准则之一。高可用性系统意味着系统服务可以更长时间运行,通常通过提高系统的容错能力来实现。
高可用性或者高可靠度的系统不会希望有单点故障造成整体故障的情形。一般可以通过冗余的方式增加多个相同机能的部件,只要这些部件没有同时失效,系统(或至少部分系统)仍可运作,这会让可靠度提高。
(5)集群可用性评判标准(x 个 9)
在系统的高可用性里有个衡量其可靠性的标准------X 个 9,这个 X 是代表数字 3-5。X 个 9 表示在系统 1 年时间的使用过程中,系统可以正常使用时间与总时间(1 年)之比。
- 3个9:(1-99.9%)36524=8.76小时,表示该系统在连续运行1年时间里最多可能的业务中断时间是8.76小时。
- 4个9:(1-99.99%)36524=0.876小时=52.6分钟,表示该系统在连续运行1年时间里最多可能的业务中断时间是52.6分钟。
- 5个9:(1-99.999%)365 24*60=5.26分钟,表示该系统在连续运行1年时间里最多可能的业务中断时间是5.26分钟。
可以看出,9 越多,系统的可靠性越强,能够容忍的业务中断时间越少,但是要付出的成本更高。
2、HA 系统设计核心问题
(1)脑裂问题
脑裂(split-brain)是指"大脑分裂",本是医学名词。在HA集群中,脑裂指的是当联系主备节点的"心跳线"断开时(即两个节点断开联系时),本来为一个整体、动作协调的 HA 系统,就分裂成为两个独立的节点。由于相互失去了联系,主备节点之间像"裂脑人"一样,使得整个集群处于混乱状态。
脑裂的严重后果:
- 集群无主:都认为对方是状态好的,自己是备份角色,后果是无服务;
- 集群多主:都认为对方是故障的,自己是主角色。相互争抢共享资源,结果会导致系统混乱,数据损坏。此外对于客户端访问也是一头雾水,找谁呢?
避免脑裂问题的核心是:保持任意时刻系统有且只有一个主角色提供服务。
(2)数据状态同步问题
主备切换保证服务持续可用性的前提是主备节点之间的状态、数据是一致的,或者说准一致的。如果说备用的节点和主节点之间的数据差距过大,即使完成了主备切换的动作,那也是没有意义的。
数据同步常见做法是:通过日志重演操作记录。主角色正常提供服务,发生的事务性操作通过日志记录,备用角色读取日志重演操作。
3、HDFS NAMENODE 单点故障问题
在 Hadoop 2.0.0 之前,NameNode 是 HDFS 集群中的单点故障(SPOF)。每个群集只有一个 NameNode,如果 NameNode 进程不可用,则整个 HDFS 群集不可用。
NameNode 的单点故障从两个方面影响了 HDFS 群集的总可用性:
- 如果发生意外事件(例如机器崩溃),则在重新启动 NameNode 之前,群集将不可用。
- 计划内的维护事件,例如 NameNode 计算机上的软件或硬件升级,将导致群集停机时间的延长。
解决方案:在同一群集中运行两个(从 3.0.0 起,支持超过两个)冗余 NameNode。形成主备架构。这样可以在机器崩溃的情况下快速故障转移到新的 NameNode,或者出于计划维护的目的由管理员发起的正常故障转移。
4、HDFS HA 解决方案 -- QJM
仲裁日志管理器(Quorum Journal Manager,QJM)是 Hadoop 官方推荐的 HDFS HA 解决方案之一。
使用 zookeeper 中 ZKFC 来实现主备切换;使用 Journal Node(JN)集群实现 edits log 的共享以达到数据同步的目的。
4.1 ZKFailoverController(zkfc)
Apache ZooKeeper 是一款高可用分布式协调服务软件,用于维护少量的协调数据。Zookeeper 的下列特性功能参与了 HDFS 的 HA 解决方案中:
- Path路径唯一性:zookeeper 中维持了一份类似目录树的数据结构。每个节点称之为 Znode。Znode 具有唯一性,不会重名。也可以理解为排他性。
- 临时 znode:如果一个 znode 节点是临时的,那么该 znode 的生命周期将和创建它的客户端的 session 绑定。客户端断开连接 session 结束,znode 将会被自动删除。
- 监听机制:客户端可以针对 znode 上发生的事件设置监听,当事件发生触发条件,zk 服务会把事件通知给设置监听的客户端。
ZKFailoverController(ZKFC)是一个新组件,它是一个 ZooKeeper 客户端。运行 NameNode 的每台计算机也都运行 ZKFC,ZKFC 的主要职责: - 监视和管理 NameNode 健康状态
ZKFC通过命令监视的 NameNode 节点及机器的健康状态。 - 维持和 ZK 集群联系
如果本地 NameNode 运行状况良好,并且 ZKFC 看到当前没有其他节点持有锁 znode,它将自己尝试获取该锁。如果成功,则表明它"赢得了选举",并负责运行故障转移以使其本地NameNode 处于 Active 状态。如果已经有其他节点持有锁,zkfc 选举失败,则会对该节点注册监听,等待下次继续选举。
4.2 Fencing(隔离)机制
故障转移过程也就是俗称的主备角色切换的过程,切换过程中最怕的就是脑裂的发生。因此需要 Fencing 机制来避免,将先前的 Active 节点隔离,然后将 Standby 转换为 Active 状态。
Hadoop 公共库中对外提供了两种 Fenching 实现,分别是 sshfence 和 shellfence(缺省实现)。
- sshfence 是指通过 ssh 登陆目标节点上,使用命令 fuser 将进程杀死(通过 tcp 端口号定位进程 pid,该方法比 jps 命令更准确);
- shellfence 是指执行一个用户事先定义的 shell 命令(脚本)完成隔离
4.3 主备数据状态同步问题解决
Journal Node(JN)集群是轻量级分布式系统,主要用于高速读写数据、存储数据。通常使用 2N+1 台 JournalNode 存储共享 Edits Log(编辑日志)。底层类似于 zk 的分布式一致性算法。
任何修改操作在 Active NN 上执行时,JournalNode 进程同时也会记录 edits log 到至少半数以上的 JN 中,这时 Standby NN 监测到 JN 里面的同步 log 发生变化了会读取 JN 里面的 edits log,然后重演操作记录同步到自己的目录镜像树里面。
当发生故障 Active NN 挂掉后,Standby NN 会在它成为 Active NN 前,读取所有的 JN 里面的修改日志,这样就能高可靠的保证与挂掉的 NN 的目录镜像树一致,然后无缝的接替它的职责,维护来自客户端请求,从而达到一个高可用的目的。
5、YARN HA
5.1 YARN HA 背景
ResourceManager 负责资源管理和应用的调度,是 YARN 的核心组件,集群的主角色。在 Hadoop 2.4 之前,ResourceManager 是 YARN 群集中的 SPOF(Single Point of Failure,单点故障)。为了解决 RM 的单点故障问题,YARN 设计了一套 Active/Standby 模式的 ResourceManager HA 架构。
- Active RM:处理客户端请求、调度资源、管理 NodeManager。
- Standby RM:实时同步主 RM 的状态,随时准备接管。
在运行期间有多个 ResourceManager 同时存在来增加冗余进而消除这个单点故障,并且只能有一个 ResourceManager 处于 Active 状态,其他的则处于 Standby 状态,当 Active 节点无法正常工作,其余 Standby 状态的几点则会通过竞争选举产生新的 Active 节点。
5.2 故障转移机制
- 手动故障转移:管理员使用命令手动进行状态切换。
- 自动故障转移:RM 可以选择嵌入基于 Zookeeper 的 ActiveStandbyElector线程来实现自动故障转移。
Hadoop 官方推荐方案:基于 Zookeeper 集群实现 YARN HA。 实现 HA 集群的关键是:主备之间状态数据同步、主备之间顺利切换(故障转移机制)针对数据同步问题,可以通过 zk 来存储共享集群的状态数据。因为 zk 本质也是一个小文件存储系统。针对主备顺利切换,可以手动,也可以基于 zk 自动实现。
5.3 故障转移原理(基于 zk 自动切换)
- 创建锁节点:在 ZooKeeper 上会创建一个叫做 ActiveStandbyElectorLock 的锁节点,所有的 RM 在启动的时候,都会去竞争写这个临时的 Lock 节点,而 ZooKeeper 能保证只有一个 RM 创建成功。创建成功的 RM 就切换为 Active 状态,没有成功的 RM 则切换为 Standby 状态。
- 注册Watcher监听:Standby 状态的 RM 向 ActiveStandbyElectorLock 节点注册一个节点变更的 Watcher 监听,利用临时节点的特性(会话结束节点自动消失),能够快速感知到 Active 状态的 RM 的运行情况。
- 准备切换:当 Active 状态的 RM 出现故障(如宕机或网络中断),其在 ZooKeeper 上创建的 Lock 节点随之被删除,这时其它各个 Standby 状态的 RM 都会受到 ZooKeeper 服务端的 Watcher 事件通知,然后开始竞争写 Lock 子节点,创建成功的变为 Active 状态,其他的则是 Standby 状态。
- Fencing(隔离):在分布式环境中,机器经常出现假死的情况(常见的是 GC 耗时过长、网络中断或 CPU 负载过高)而导致无法正常对外进行及时响应。如果有一个处于 Active 状态的 RM 出现假死,其他的 RM 刚选举出来新的 Active 状态的 RM,这时假死的 RM 又恢复正常,还认为自己是 Active 状态,这就是分布式系统的脑裂现象,即存在多个处于 Active 状态的 RM,可以使用隔离机制来解决此类问题。
YARN 的 Fencing 机制是借助 ZooKeeper 数据节点的 ACL 权限控制来实现不同 RM 之间的隔离。创建的根 ZNode 必须携带 ZooKeeper 的 ACL 信息,目的是为了独占该节点,以防止其他 RM 对该 ZNode 进行更新。借助这个机制假死之后的 RM 会试图去更新 ZooKeeper 的相关信息,但发现没有权限去更新节点数据,就把自己切换为 Standby 状态。
5.3 YARN HA 的配置步骤
(1)核心参数配置
在 yarn-site.xml 中设置以下参数:
xml
<!-- 启用 RM HA -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 定义 RM 集群的 ID -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>my-yarn-cluster</value>
</property>
<!-- 列出所有 RM 的 ID -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- 配置每个 RM 的主机名和端口 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>rm1-host</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>rm2-host</value>
</property>
<!-- 使用 ZooKeeper 实现状态存储和故障转移 -->
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>zk1:2181,zk2:2181,zk3:2181</value>
</property>
(2)NodeManager 配置
所有 NodeManager 需配置所有 RM 地址,以便自动切换:
xml
<property>
<name>yarn.resourcemanager.address.rm1</name>
<value>rm1-host:8032</value>
</property>
<property>
<name>yarn.resourcemanager.address.rm2</name>
<value>rm2-host:8032</value>
</property>
(3)客户端配置
客户端需支持自动发现活动 RM:
xml
<property>
<name>yarn.resourcemanager.address</name>
<value>rm1-host:8032,rm2-host:8032</value>
</property>
5.4 验证 HA 功能
手动触发故障转移:
shell
yarn rmadmin -transitionToStandby -rmid rm1 # 将 rm1 切换为 Standby
yarn rmadmin -transitionToActive -rmid rm2 # 将 rm2 切换为 Active
模拟主 RM 宕机:关闭主 RM 进程,观察备用 RM 是否自动接管。
6、YARN RM 重启机制
ResourceManager 负责资源管理和应用的调度,是 YARN 的核心组件,存在单点故障的问题。ResourceManager Restart 重启机制是使 RM 在重启动时能够使 Yarn 集群正常工作的特性,并且使 RM 的出现的失败不被用户知道。重启机制并不是自动帮我们重启的意思(需要手动启动 RM)。所以说不能解决单点故障问题。
6.1 YARN RM 重启步骤
shell
# 关闭 yarn rm:
yarn --daemon stop resourcemanager
# 启动 yarn rm
yarn --daemon start resourcemanager
6.2 YARN RM 重启策略
- Non-work-preserving RM restart
- 当 Client 提交一个 application 给 RM 时,RM 会将该 application 的相关信息存储起来,具体存储的位置是可以在配置文件中指定的,可以存储到本地文件系统上,也可以存储到 HDFS 或者是 Zookeeper 上,此外 RM 也会保存 application 的最终状态信息(failed,killed,finished),如果是在安全环境下运行,RM 还会保存相关证书文件。
- 当 RM 被关闭后,NodeManager(以下简称 NM)和 Client 由于发现连接不上 RM,会不断的向 RM 发送消息,以便于能及时确认 RM 是否已经恢复正常,当 RM 重新启动后,它会发送一条re-sync (重新同步)的命令给所有的 NM 和 ApplicationMaster(以下简称 AM),NM 收到重新同步的命令后会杀死所有的正在运行的 containers 并重新向 RM 注册,从 RM 的角度来看,每台重新注册的 NM 跟一台新加入到集群中 NM 是一样的。
- AM 收到重新同步的命令后会自行将自己杀掉。接下来,RM 会将存储的关于 application 的相关信息读取出来,将在 RM 关闭之前最终状态为正在运行中的 application 重新提交运行。
- Work-preserving RM restart
- 与不保留工作不同的地方在于,RM 会记录下 container 的整个生命周期的数据,包括 application 运行的相关数据,资源申请状况,队列资源使用状况等数据。
- 当 RM 重启之后,会读取之前存储的关于 application 的运行状态的数据,同时发送 re-sync 的命令,与第一种方式不同的是,NM 在接受到重新同步的命令后并不会杀死正在运行的 containers,而是继续运行 containers 中的任务,同时将 containers 的运行状态发送给 RM,之后,RM 根据自己所掌握的数据重构 container 实例和相关的 application 运行状态,如此一来,就实现了在 RM 重启之后,紧接着 RM 关闭时任务的执行状态继续执行。
6.3 RM 状态数据的存储介质
如果启用了 RM 的重启机制,升级为 Active 状态的 RM 会初始化 RM 内部状态和恢复先前活动 RM 留下的状态,这依赖于 RM 的重启特性。而之前提交到 RM 托管的作业会发起新的尝试请求。用户提交的应用可以考虑进行周期性的 CheckPoint 来避免任务丢失。
RM 的重启机制本质上是将 RM 内部的状态信息写入外部存储介质中。在 RM 启动时会初始化状态信息的目录,当 Application 运行时会将相关的状态写入对应的目录下。如果 RM 发生故障切换或者重启,可以通过外部存储进行恢复。RM 状态存储的实现是 RMStateStore 抽象类,YARN 对 RMStateStore 提供了几种实例:
状态存储方式 | 说明 |
---|---|
Memory | MemoryRMStateStore 是基于内存的状态存储实现,使用 RMState 对象存储 RM 所有的状态。 |
ZooKeeper | ZKRMStateStore 是基于 ZooKeeper 的状态存储实现,支持 RM 的 HA,只有基于ZooKeeper 的状态存储支持隔离机制,能避免出现裂脑情况发生,允许有多个处于 Active 状态的 RM 同时编辑状态存储。建议在 YARN 的 HA 中使用。 |
FileSystem | FileSystemRMStateStore 支持 HDFS 和基于本地 FS 的状态存储实现。不支持隔离机制。 |
LevelDB | LeveldbRMStateStore 是基于 LevelDB 的状态存储实现,它比基于 HDFS 和 ZooKeeper 的状态存储库更轻巧。LevelDB 支持更好的原子操作,每个状态更新的 I/O 操作更少,文件系统上的文件总数也少得多。不支持隔离机制。 |
Null | NullRMStateStore 是一个空实现。 |
(1)ZKRMStateStore | |
RM 的所有状态信息存储在 ZooKeeper 的 /rmstore/ZKRMStateRoot 下;主要保存了 RM 资源预留信息、应用信息,应用的 Token 信息,RM 版本信息。 |
|
ZNode名称 | 说明 |
:-- | :-- |
ReservationSystemRoot | RM 的资源预留系统,对应的实现是 ReservationSystem 接口的子类。 |
RMAppRoot | Application 信息,对应的实现是 RMApp 接口的子类。 |
AMRMTokenSecretManagerRoot | ApplicationAttempt 的 Token 信息,RM 会将每个 Token 保存在本地的内存中,直到应用程序运行完成为止,并保存到 ZooKeeper 存储以供重新启动。对应的实现是 AMRMTokenSecretManager 类。 |
EpochNode | RM 的保存工作重启的时间信息。每次 RM 重新启动时,纪元都会增加。它用于确保 ContainerId 的唯一性。对应的实现是 Epoch 抽象类。 |
RMDTSecretManagerRoot | 一个特定于 RM 的委托令牌保密管理器。保密管理器负责生成和接受每个令牌的密码。 |
RMVersionNode | RM 的版本信息。 |
6.4 配置
yarn-site.xml
xml
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>zk1:2181,zk2:2181,zk3:2181</value>
</property>
七、分布式HA集群搭建
1、HA 集群部署规划
理论上 YARN 集群可以部署在任意机器上,但是实际中,通常把 NodeManager 和 DataNode 部署在同一台机器上。(有数据的地方就有可能产生计算,移动程序的成本比移动数据的成本低)
作为 Apache Hadoop 的一部分,通常会把 YARN 集群和 HDFS 集群一起搭建。
IP | 主机名 | 服务 |
---|---|---|
192.168.170.136 | rl-node1 | datanode、namenode、nodemanager、resourcemanager、zookeeper、journal node、zkfc |
192.168.170.137 | rl-node2 | datanode、namenode、nodemanager、resourcemanager、zookeeper、journal node、zkfc |
192.168.170.138 | rl-node3 | datanode、nodemanage、zookeeper、journal node |
2、集群部署
环境准备、Hadoop安装参照第五点,这里仅说明应该修改哪些配置文件.
配置文件在 hadoop-3.4.0/etc/hadoop 目录下。
-
workers
xmlrl-node1 rl-node2 rl-node3
-
shell
export JAVA_HOME=/app/jdk1.8.0_431 export HADOOP_HOME=/app/hadoop-3.4.0 export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export HADOOP_LOG_DIR=$HADOOP_HOME/logs
-
core-site.xml
xml<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property><!-- HA 集群名称,该值要和 hdfs-site.xml 中的配置保持一致 --> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property> <property><!-- hadoop 本地数据存储目录 format 时自动生成 --> <name>hadoop.tmp.dir</name> <value>/app/hadoop-3.4.0/data/tmp</value> </property> <property><!-- 在 Web UI 访问 HDFS 使用的用户名。--> <name>hadoop.http.staticuser.user</name> <value>hadoop</value> </property> <property><!-- ZooKeeper 集群的地址和端口--> <name>ha.zookeeper.quorum</name> <value>rl-node1:2181,rl-node2:2181,rl-node3:2181</value> </property> </configuration>
-
hdfs-site.xml
xml<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <!--指定 hdfs 的 nameservice 为 mycluster,需要和 core-site.xml 中的保持一致 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> </property> <!-- mycluster 下面有两个 NameNode,分别是 nn1,nn2 --> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property> <!-- nn1 的 RPC 通信地址 --> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>rl-node1:8020</value> </property> <!-- nn1 的 http 通信地址 --> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>rl-node1:9870</value> </property> <!-- nn2 的 RPC 通信地址 --> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>rl-node2:8020</value> </property> <!-- nn2 的 http 通信地址 --> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>rl-node2:9870</value> </property> <!-- 指定 NameNode 的 edits 元数据在 JournalNode 上的存放位置 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://rl-node1:8485;rl-node2:8485;rl-node3:8485/mycluster</value> </property> <!-- 指定 JournalNode 在本地磁盘存放数据的位置 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/app/hadoop-3.4.0/data/journaldata</value> </property> <!-- 开启 NameNode 失败自动切换 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- 指定该集群出故障时,哪个实现类负责执行故障切换 --> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!-- 配置隔离机制方法--> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <!-- 使用 sshfence 隔离机制时需要 ssh 免登陆 --> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/hadoop/.ssh/id_rsa</value> </property> <!-- 配置 sshfence 隔离机制超时时间 --> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> <!-- 开启短路本地读取功能 --> <property> <name>dfs.client.read.shortcircuit</name> <value>true</value> </property> <!-- 指定 DataNode 使用的域套接字(domain socket)的路径,需手动创建目录 mkdir -p /var/lib/hadoop-hdfs --> <property> <name>dfs.domain.socket.path</name> <value>/var/lib/hadoop-hdfs/dn_socket</value> </property> <!-- 开启黑名单 --> <property> <name>dfs.hosts.exclude</name> <value>/app/hadoop-3.4.0/etc/hadoop/excludes</value> </property> <property> <!-- 允许NameNode仅接受来自指定主机列表的数据块报告和心跳 --> <name>dfs.namenode.name.hosts</name> <value>rl-node1,rl-node2,rl-node3</value> </property> <property> <!-- 指定DataNode上数据目录的Unix文件权限 --> <name>dfs.datanode.data.dir.perm</name> <value>700</value> </property> <property> <!-- 指定NameNode用于处理客户端请求的线程数 --> <name>dfs.namenode.handler.count</name> <value>100</value> </property> <property> <!-- 配置HDFS块的大小 --> <name>dfs.blocksize</name> <value>268435456</value> </property> </configuration>
-
yarn-site.xml
xml<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <!-- 启用RM HA --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <!-- RM HA集群标识ID --> <property> <name>yarn.resourcemanager.cluster-id</name> <value>yarn_cluster</value> </property> <!-- RM HA集群中各RM的逻辑标识 --> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <!-- rm1运行主机 --> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>rl-ndoe1</value> </property> <!-- rm2运行主机 --> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>rl-node2</value> </property> <!-- rm1 WebUI地址 --> <property> <name>yarn.resourcemanager.webapp.address.rm1</name> <value>rl-node1:8088</value> </property> <!-- rm2 WebUI地址 --> <property> <name>yarn.resourcemanager.webapp.address.rm2</name> <value>rl-node2:8088</value> </property> <!-- 开启自动故障转移 --> <property> <name>yarn.resourcemanager.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- NodeManager上运行的附属服务。需配置成mapreduce_shuffle,才可运行MR程序。--> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <!-- 每个容器请求的最小内存资源(以MB为单位)。--> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>512</value> </property> <!-- 每个容器请求的最大内存资源(以MB为单位)。--> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>2048</value> </property> <!-- 容器虚拟内存与物理内存之间的比率。--> <property> <name>yarn.nodemanager.vmem-pmem-ratio</name> <value>4</value> </property> <!-- 指定NodeManager用于存储本地数据的目录路径。 --> <property> <name>yarn.nodemanager.local-dirs</name> <value>/app/hadoop-3.4.0/data/nm-local</value> </property> <!-- 指定NodeManager用于存储应用程序日志的目录路径 --> <property> <name>yarn.nodemanager.log-dirs</name> <value>/app/hadoop-3.4.0/data/nm-log</value> </property> <!-- 开启 yarn 日志聚集功能,收集每个容器的日志集中存储在一个地方 --> <property> <name>yarn.log-aggregation-enable</name> <value>true</value> </property> <!-- 指定当应用程序运行结束后,日志被转移到的HDFS目录 --> <property> <name>yarn.nodemanager.remote-app-log-dir</name> <value>/tmp/logs</value> </property> <!-- 日志保留时间设置为一天 --> <property> <name>yarn.log-aggregation.retain-seconds</name> <value>86400</value> </property> <!-- 定义YARN日志服务器的访问地址 --> <property> <name>yarn.log.server.url</name> <value>http://rl-node1:19888/jobhistory/logs</value> </property> <!-- 指定YARN Web应用程序代理服务器的主机名和端口号 --> <property> <name>yarn.web-proxy.address</name> <value>node1:8089</value> </property> <!-- zk 集群 --> <property> <name>hadoop.zk.address</name> <value>rl-node1:2181,rl-node2:2181,rl-node3:2181</value> </property> <!-- 开启rm状态恢复重启机制 --> <property> <name>yarn.resourcemanager.recovery.enabled</name> <value>true</value> </property> <!-- 使用zk集群存储RM状态数据 --> <property> <name>yarn.resourcemanager.store.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value> </property> </configuration>
九、Hadoop 面试题
1、 题目
Hadoop核心概念
- Hadoop的核心组件是什么?简要说明每个组件的作用。
- Hadoop 1.x和Hadoop 2.x架构的主要区别是什么?
- HDFS的设计目标和优缺点是什么?
- 解释HDFS的写流程和读流程。
- 什么是机架感知(Rack Awareness)?它在HDFS中的作用是什么?
- NameNode和DataNode的职责分别是什么?Secondary NameNode的作用是什么?
- Hadoop如何实现容错性(Fault Tolerance)?
HDFS(分布式文件系统)
- 什么是HDFS的块(Block)?默认大小是多少?为什么设计成大块?
- 如何手动修复HDFS中的损坏块?
- HDFS的副本放置策略是什么?(如第一个副本、第二个副本的放置规则)
- HDFS Federation解决了什么问题?
- 如何通过命令行查看HDFS文件系统的使用情况?
- HDFS的小文件问题如何解决?
MapReduce
- 详细描述MapReduce的工作流程(从InputSplit到Shuffle再到Reduce输出)。
- MapReduce中Combiner的作用是什么?是否所有场景都适用Combiner?
- 如何自定义分区(Partitioner)?举一个实际应用场景。
- 什么是MapReduce的推测执行(Speculative Execution)?为什么需要它?
- MapReduce的Shuffle阶段具体做了什么?
- MapReduce如何处理数据倾斜(Data Skew)问题?
- MapReduce和Spark的区别是什么?
YARN(资源管理)
- YARN的核心组件是什么(ResourceManager、NodeManager、ApplicationMaster)?
- YARN如何分配和管理集群资源?
- Container在YARN中的作用是什么?
- 如何通过YARN查看和管理正在运行的作业?
- YARN的调度器有哪些(Capacity Scheduler、Fair Scheduler)?它们的区别是什么?
性能优化与调优
- 如何优化MapReduce作业的性能?(从Map/Reduce数量、压缩、Combiner等方面回答)
- Hadoop集群中常见的瓶颈是什么?如何排查?
- HDFS的写入速度较慢,可能的原因有哪些?
- 如何调整HDFS的副本数量?修改后如何生效?
- Hadoop集群的硬件配置建议(如磁盘、内存、CPU)?
Hadoop生态系统
- 列举Hadoop生态系统中常用的工具(如Hive、HBase、Spark等),并简要说明它们的用途。
- HBase和HDFS的区别是什么?
- ZooKeeper在Hadoop生态中的作用是什么?
- 什么是Sqoop和Flume?它们的典型应用场景是什么?
- Hadoop如何与云存储(如AWS S3)集成?
场景题
- 假设一个MapReduce作业卡在99%,可能的原因是什么?如何解决?
- 设计一个海量日志文件的词频统计方案(从数据存储到计算流程)。
- 如何监控Hadoop集群的健康状态?常用的工具有哪些?
- 如何实现Hadoop集群的高可用(HA)?
- 如果某个DataNode节点宕机,HDFS如何保证数据不丢失?
高级问题
- Hadoop 3.x的新特性有哪些?(如Erasure Coding、多NameNode支持)
- 什么是HDFS的纠删码(Erasure Coding)?相比副本机制有何优缺点?
- 如何通过Hadoop实现近实时数据处理?
- Hadoop在容器化(如Kubernetes)环境中的部署挑战是什么?
- Hadoop的安全机制有哪些?(如Kerberos认证、HDFS权限控制)
2、答案
Hadoop核心概念
1. Hadoop核心组件
- HDFS(Hadoop Distributed File System):分布式文件系统,负责数据存储。
- MapReduce:分布式计算框架,处理海量数据。
- YARN(Yet Another Resource Negotiator):资源管理框架,负责集群资源调度。
2. Hadoop 1.x vs 2.x
- Hadoop 1.x:仅支持MapReduce计算,资源管理由JobTracker和TaskTracker实现。
- Hadoop 2.x:引入YARN,解耦资源管理和任务调度,支持多种计算框架(如Spark、Flink)。
3. HDFS设计目标
- 目标:高容错性、高吞吐量、支持大规模数据集。
- 优点:适合离线批处理、数据冗余存储。
- 缺点:高延迟、不适合小文件存储。
4. HDFS读写流程
- 写流程 :
- 客户端向NameNode申请写入文件。
- NameNode分配DataNode列表(默认3副本)。
- 客户端直接写入第一个DataNode,DataNode之间通过管道复制数据。
- 读流程 :
- 客户端向NameNode获取文件块位置。
- 客户端直接从最近的DataNode读取数据。
5. 机架感知(Rack Awareness)
- 作用:优化副本放置策略,保证数据的可靠性和读取效率。
- 策略:第一个副本放在客户端所在机架,第二个副本放在不同机架,第三个副本放在第二个副本的同机架。
6. NameNode vs DataNode
- NameNode:管理文件元数据(如目录树、块位置)。
- DataNode:存储实际数据块。
- Secondary NameNode:辅助NameNode合并FsImage和EditLog(非热备)。
7. 容错性实现
- HDFS:数据多副本存储(默认3副本)。
- MapReduce:任务失败后自动重新调度到其他节点执行。
HDFS
8. HDFS块(Block)
- 默认大小:128MB(Hadoop 2.x+)或64MB(Hadoop 1.x)。
- 设计原因:减少元数据量、优化大文件处理效率。
9. 修复损坏块
-
使用
hdfs fsck
检查损坏块:hdfs fsck /path -files -blocks -locations
-
手动删除损坏块或触发HDFS自动修复。
10. 副本放置策略
- 默认策略 :
- 第1个副本:客户端所在节点(或随机节点)。
- 第2个副本:不同机架的节点。
- 第3个副本:与第2个副本同机架的另一节点。
11. HDFS Federation
- 解决问题:单个NameNode的内存瓶颈,支持横向扩展多个NameNode,每个管理不同的命名空间。
12. 查看HDFS使用情况
hdfs dfsadmin -report # 查看集群状态
hdfs dfs -du -h /path # 查看目录大小
13. 小文件问题
- 解决方案 :
- 合并小文件(使用HAR文件或Hive合并)。
- 使用SequenceFile、ORC等容器格式存储。
MapReduce
14. MapReduce工作流程
- InputSplit:输入文件分片,每个分片由一个Map处理。
- Map阶段:处理输入数据,输出键值对。
- Shuffle阶段:将相同Key的数据发送到同一Reduce。
- Reduce阶段:聚合Map结果,输出最终结果。
15. Combiner
- 作用:在Map端局部聚合数据,减少网络传输量。
- 限制:必须是幂等操作(如Sum、Max)。
16. 自定义Partitioner
- 场景:按业务逻辑将数据分发到不同Reduce(如按省份分区)。
- 实现 :继承
Partitioner
类,重写getPartition()
方法。
17. 推测执行
- 作用:防止慢节点拖慢整体作业,启动相同任务的备份副本。
- 配置 :
mapreduce.map.speculative=true
(默认开启)。
18. Shuffle阶段
- Map端:分区、排序、溢写(Spill)到磁盘。
- Reduce端:拉取数据、合并排序。
19. 数据倾斜处理
- 优化手段 :
- 使用Combiner减少数据传输量。
- 自定义分区策略分散热点Key。
- 增加Reduce任务数。
20. MapReduce vs Spark
- MapReduce:基于磁盘计算,适合离线批处理。
- Spark:基于内存迭代计算,适合实时和迭代任务。
YARN
21. YARN核心组件
- ResourceManager(RM):全局资源调度。
- NodeManager(NM):单节点资源管理。
- ApplicationMaster(AM):单个作业的任务调度和容错。
22. 资源分配
- 资源模型:基于Container(封装CPU、内存资源)。
- 流程:AM向RM申请资源,NM启动Container执行任务。
23. Container
- 作用:YARN的资源单位,每个任务(Map/Reduce)运行在Container中。
24. 管理YARN作业
yarn application -list # 查看作业列表
yarn application -kill <application_id> # 终止作业
25. 调度器对比
- Capacity Scheduler:按队列分配资源,保证队列最小资源。
- Fair Scheduler:动态分配资源,保证所有作业公平共享资源。
性能优化
26. MapReduce优化
- 调整Map/Reduce数量:避免过多或过少。
- 使用压缩:减少磁盘IO(如Snappy压缩中间数据)。
- 启用Combiner:减少Shuffle数据量。
27. 集群瓶颈
- 常见瓶颈:磁盘IO、网络带宽、CPU负载。
- 排查工具:HDFS日志、YARN ResourceManager UI、Ganglia监控。
28. HDFS写入慢
- 原因:网络拥堵、DataNode负载高、副本复制策略不合理。
29. 调整副本数量
- 修改
hdfs-site.xml
中的dfs.replication
参数。 - 生效方式:对已有文件需手动执行
hdfs dfs -setrep -w 3 /path
。
Hadoop生态系统
31. 常用工具
- Hive:SQL化数据仓库。
- HBase:分布式NoSQL数据库。
- Spark:内存计算框架。
- Sqoop:关系数据库与Hadoop间数据传输。
- Flume:日志数据采集。
32. HBase vs HDFS
- HBase:低延迟随机读写(OLTP)。
- HDFS:高吞吐批处理(OLAP)。
33. ZooKeeper作用
- 分布式协调服务,用于HBase Master选举、YARN高可用等场景。
34. Sqoop与Flume
- Sqoop:批量导入关系型数据库数据到Hadoop。
- Flume:实时采集日志数据到HDFS/HBase。
场景题
36. MapReduce作业卡在99%
- 原因:最后一个Reduce任务慢或失败。
- 解决:检查日志、调整Reduce数量、关闭推测执行。
37. 词频统计方案
- 数据存储:原始日志存入HDFS。
- Map阶段:拆分单词,输出
<word, 1>
。 - Reduce阶段:聚合相同单词的计数。
39. Hadoop高可用(HA)
- NameNode HA:通过ZooKeeper实现主备切换。
- ResourceManager HA:配置Active/Standby RM。
40. DataNode宕机处理
- HDFS自动从其他副本恢复数据。
- 手动触发
hdfs fsck
修复。
高级问题
41. Hadoop 3.x新特性
- 纠删码(Erasure Coding):替代副本机制,节省存储空间(如RS-10-4编码)。
- 多NameNode支持:进一步扩展HDFS Federation。
42. 纠删码 vs 副本
- 优点:存储开销从300%降低到150%。
- 缺点:恢复计算开销大,适合冷数据存储。
45. Hadoop安全机制
- Kerberos认证:防止未授权访问。
- HDFS ACL:细粒度文件权限控制。
常用命令
-
文件操作:
hadoop fs -put localfile /hdfs/path # 上传文件 hadoop fs -cat /hdfs/path/file # 查看文件内容
-
作业管理:
yarn logs -applicationId <app_id> # 查看作业日志