前言:一个关于"抢资源"的故事
在讲YARN之前,让我先讲一个真实的故事。
那是Hadoop 1.x的时代,我们公司有一个50台机器的Hadoop集群。某天,数据组的小王提交了一个MapReduce任务,跑全年的用户行为分析;与此同时,算法组的小李也提交了一个任务,训练推荐模型。
然后,整个集群就陷入了混乱——两个任务疯狂抢资源,互相等待,谁也跑不完。
更惨的是,运维组的老张想跑一个Spark任务做实时监控,但是——对不起,Hadoop 1.x只支持MapReduce,不支持Spark。
老张当场表演了一个原地裂开。
"如果有一个统一的资源管理系统,能管理所有类型的计算任务,该多好啊……"
于是,YARN诞生了。
本篇是Hadoop系列的最后一篇,我们将学习:
- YARN:Hadoop的资源管理系统
- Hadoop生态圈:大象的朋友们
让我们开始最后的冲刺!
一、从Hadoop 1.x到2.x:架构的进化
1.1 Hadoop 1.x的架构问题
作业调度 + 资源管理
一个人干两份活] TT1[TaskTracker1] TT2[TaskTracker2] TT3[TaskTracker3] end subgraph HDFS1["HDFS"] NN[NameNode] DN1[DataNode1] DN2[DataNode2] end JT --> TT1 JT --> TT2 JT --> TT3 end subgraph 问题["⚠️ 存在的问题"] P1[1. JobTracker单点故障
挂了全完蛋] P2[2. JobTracker负担太重
既管资源又管调度] P3[3. 只支持MapReduce
想跑Spark?没门] P4[4. 资源利用率低
slot固定分配] end style JT fill:#ff6b6b style P1 fill:#ff6b6b style P2 fill:#ff6b6b style P3 fill:#ff6b6b style P4 fill:#ff6b6b
Hadoop 1.x的核心问题就是:JobTracker一个人干了太多活,又要管理集群资源,又要调度MapReduce任务。这就像让一个程序员既写代码又做产品又搞运维还要兼职HR——迟早要猝死。
1.2 Hadoop 2.x的解决方案:YARN
Hadoop 2.x做了一个聪明的决定:把资源管理和作业调度分开。
全局资源管理] NM1[NodeManager1] NM2[NodeManager2] NM3[NodeManager3] end subgraph Apps["应用层 (各种计算框架)"] MR[MapReduce] Spark[Spark] Flink[Flink] Tez[Tez] end subgraph HDFS2["HDFS"] NN[NameNode] DN1[DataNode1] DN2[DataNode2] end Apps --> YARN YARN --> HDFS2 end subgraph 优势["🎉 改进"] A1[1. 资源管理与作业调度分离] A2[2. 支持多种计算框架] A3[3. 资源利用率提高] A4[4. 可扩展性更强] end style RM fill:#4ecdc4 style A1 fill:#4ecdc4 style A2 fill:#4ecdc4 style A3 fill:#4ecdc4 style A4 fill:#4ecdc4
YARN = Yet Another Resource Negotiator(又一个资源协调者)
这个名字起得很谦虚——"又一个",但它确实彻底改变了Hadoop的命运。
二、YARN架构详解
2.1 核心组件
资源管理者
大管家] end subgraph RM内部["ResourceManager内部"] Scheduler[Scheduler
调度器
只负责分配资源] ASM[ApplicationsManager
应用管理器
管理所有应用] end subgraph 节点["每个节点"] NM1[NodeManager
节点管理器
管理单个节点资源] NM2[NodeManager] NM3[NodeManager] end subgraph 应用["每个应用"] AM1[ApplicationMaster
应用主管
管理单个应用] C1[Container
资源容器] C2[Container] C3[Container] end RM --> Scheduler RM --> ASM RM --> NM1 RM --> NM2 RM --> NM3 ASM --> AM1 AM1 --> C1 AM1 --> C2 AM1 --> C3 NM1 -.-> C1 NM2 -.-> C2 NM3 -.-> C3 end style RM fill:#ff6b6b,stroke:#333,stroke-width:2px style NM1 fill:#4ecdc4 style NM2 fill:#4ecdc4 style NM3 fill:#4ecdc4 style AM1 fill:#ffe66d style C1 fill:#95a5a6 style C2 fill:#95a5a6 style C3 fill:#95a5a6
2.2 各组件职责详解
ResourceManager(RM):大管家
ResourceManager的哲学:我只管分配资源,具体怎么用是你的事。
这就像公司的财务部门:你申请预算,我审批拨款,但钱怎么花是你项目组自己的事。
NodeManager(NM):节点保姆
NodeManager的哲学:我就是个包工头,管好我这一亩三分地。
ApplicationMaster(AM):应用管家
ApplicationMaster的哲学:我的应用我做主,需要什么资源我去要。
这是YARN最精妙的设计——每个应用有自己的管家。MapReduce有MapReduce的AM,Spark有Spark的AM,各管各的,互不干扰。
Container:资源容器
资源抽象] subgraph 包含["包含资源"] R1[CPU核数] R2[内存大小] R3[磁盘空间] R4[网络带宽] end subgraph 特点["特点"] F1[动态分配] F2[隔离执行] F3[可以运行任何程序] end end C --> R1 C --> R2 C --> R3 C --> R4 style C fill:#95a5a6
Container的本质:就是一个资源的"盒子",里面装着CPU和内存,你的任务就在这个盒子里运行。
2.3 YARN作业提交流程
让我们看看一个MapReduce作业是如何在YARN上运行的:
用大白话复述一遍:
| 步骤 | 发生了什么 | 类比 |
|---|---|---|
| 1-2 | 客户端把代码和数据传上去,然后告诉RM:"我要跑一个任务" | 向公司提交项目申请 |
| 3-7 | RM找一台机器启动AM,AM开始规划任务 | HR给你分配一个项目经理 |
| 8-11 | AM向RM要资源,拿到后通知NM启动任务 | 项目经理申请预算和人力 |
| 12-14 | 任务跑着,各方互相汇报进度 | 项目周报 |
| 15-16 | 任务完成,资源释放 | 项目结项,人员释放 |
三、YARN调度器:资源怎么分?
3.1 三种调度器对比
YARN支持三种调度器,解决的核心问题是:集群资源有限,多个应用抢资源,怎么分配?
3.2 FIFO Scheduler:简单但不实用
问题:Job1跑了100秒,Job2和Job3只能干等着。即使它们只需要10%的资源,也得排队。
这就像食堂只有一个窗口,前面有人买100个包子,你买1个也得等他买完。
3.3 Capacity Scheduler:容量保证
100%] Root --> QA[Queue A
40%容量
数据团队] Root --> QB[Queue B
60%容量
算法团队] QA --> QA1[子队列A1
50% of A] QA --> QA2[子队列A2
50% of A] QB --> QB1[子队列B1
70% of B] QB --> QB2[子队列B2
30% of B] end subgraph 特性["💡 特性"] T1[每个队列有最小容量保证] T2[空闲资源可以借给其他队列] T3[支持队列层级结构] T4[可以限制用户/应用的资源使用] end style Root fill:#ff6b6b style QA fill:#4ecdc4 style QB fill:#ffe66d
容量调度器的配置示例:
<!-- capacity-scheduler.xml -->
<configuration>
<!-- 定义队列 -->
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>data,algo</value>
</property>
<!-- data队列容量40% -->
<property>
<name>yarn.scheduler.capacity.root.data.capacity</name>
<value>40</value>
</property>
<!-- algo队列容量60% -->
<property>
<name>yarn.scheduler.capacity.root.algo.capacity</name>
<value>60</value>
</property>
<!-- data队列最大可使用70%(可以借用空闲资源) -->
<property>
<name>yarn.scheduler.capacity.root.data.maximum-capacity</name>
<value>70</value>
</property>
</configuration>3.4 Fair Scheduler:公平分配
Fair Scheduler的核心思想:在任何时刻,所有运行的应用平均分配资源。
支持抢占:如果某个应用占用资源超过其公平份额,调度器可以"抢回"一部分资源给其他应用。
3.5 调度器选择建议
配置多个队列] Q2 --> |"需要"|A3[Fair Scheduler
公平+抢占] Q3{使用什么发行版?} Q3 --> |"Apache原生"|A4[默认Capacity] Q3 --> |"CDH/HDP"|A5[默认Fair] end style A2 fill:#4ecdc4 style A3 fill:#ffe66d
四、YARN高可用(HA)
和HDFS一样,ResourceManager也有单点故障问题。YARN HA的解决方案:
正在服务] end subgraph Standby["备节点"] SRM[Standby RM
热备待命] end subgraph ZK["ZooKeeper集群"] ZK1[ZK1] ZK2[ZK2] ZK3[ZK3] end subgraph StateStore["状态存储"] SS[ZK / HDFS / LevelDB
存储应用状态] end subgraph NMs["NodeManager集群"] NM1[NM1] NM2[NM2] NM3[NM3] end end ARM --> ZK1 SRM --> ZK1 ARM --> SS SRM --> SS ARM --> NM1 ARM --> NM2 ARM --> NM3 style ARM fill:#4ecdc4,stroke:#333,stroke-width:2px style SRM fill:#ffe66d
故障切换流程:
五、YARN常用命令
5.1 应用管理命令
# 查看所有应用
yarn application -list
# 查看指定状态的应用
yarn application -list -appStates RUNNING
yarn application -list -appStates FINISHED
yarn application -list -appStates KILLED
# 杀死应用
yarn application -kill application_1234567890123_0001
# 查看应用状态
yarn application -status application_1234567890123_0001
# 查看应用日志
yarn logs -applicationId application_1234567890123_0001
yarn logs -applicationId application_1234567890123_0001 -containerId container_1235.2 集群管理命令
# 查看集群状态
yarn cluster -status
# 查看节点列表
yarn node -list
yarn node -list -all
yarn node -list -states RUNNING
# 查看节点详情
yarn node -status node1:8041
# 查看队列信息
yarn queue -status default5.3 资源管理命令
# 刷新队列配置(不重启生效)
yarn rmadmin -refreshQueues
# 刷新节点
yarn rmadmin -refreshNodes
# 查看ResourceManager状态(HA模式)
yarn rmadmin -getServiceState rm1
yarn rmadmin -getServiceState rm2
# 手动切换Active RM
yarn rmadmin -transitionToActive rm2 --forcemanual六、Hadoop生态圈:大象的朋友们
终于到了最精彩的部分——Hadoop生态圈。Hadoop不是一个人在战斗,它有一群强大的小伙伴。
6.1 生态圈全景图
日志采集] Sqoop[Sqoop
关系数据库导入导出] Kafka[Kafka
消息队列] NiFi[NiFi
数据流] end subgraph 存储层["💾 存储层"] HDFS[HDFS
分布式文件系统] HBase[HBase
列式NoSQL数据库] Kudu[Kudu
列式存储引擎] end subgraph 计算层["⚡ 计算层"] MR[MapReduce
批处理] Spark[Spark
内存计算] Flink[Flink
流批一体] Tez[Tez
DAG计算] end subgraph 查询层["🔍 查询层"] Hive[Hive
SQL on Hadoop] Presto[Presto
交互式查询] Impala[Impala
MPP查询引擎] Pig[Pig
数据流脚本] end subgraph 资源管理["🎛️ 资源管理"] YARN[YARN
资源调度] Mesos[Mesos
通用资源管理] K8s[Kubernetes
容器编排] end subgraph 协调服务["🔧 协调服务"] ZK[ZooKeeper
分布式协调] Oozie[Oozie
工作流调度] Airflow[Airflow
任务编排] end end 数据采集 --> 存储层 存储层 --> 计算层 计算层 --> 查询层 资源管理 --> 计算层 协调服务 --> 资源管理 style HDFS fill:#4ecdc4 style YARN fill:#4ecdc4 style Hive fill:#ffe66d style Spark fill:#ff6b6b style Flink fill:#ff6b6b style HBase fill:#9b59b6
6.2 核心组件详解
Hive:让你用SQL操作大数据
编译SQL] Driver --> Metastore[Metastore
元数据存储] Driver --> Execution[执行引擎] Execution --> |"转换成"|MR[MapReduce] Execution --> |"或"|Spark2[Spark] Execution --> |"或"|Tez2[Tez] MR --> HDFS2[HDFS] end subgraph 特点["💡 特点"] T1[SQL on Hadoop] T2[适合离线批处理] T3[延迟高,不适合交互式查询] T4[数据仓库首选] end style Hive fill:#ffe66d
Hive示例:
-- 创建表
CREATE TABLE user_behavior (
user_id STRING,
action STRING,
timestamp BIGINT
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET;
-- 查询(会被转换成MapReduce/Spark任务)
SELECT action, COUNT(*) as cnt
FROM user_behavior
WHERE dt = '2024-01-01'
GROUP BY action
ORDER BY cnt DESC
LIMIT 10;HBase:海量数据的实时读写
协调服务] ZK2 --> HMaster[HMaster
管理RegionServer] HMaster --> RS1[RegionServer1] HMaster --> RS2[RegionServer2] HMaster --> RS3[RegionServer3] RS1 --> HDFS3[HDFS] RS2 --> HDFS3 RS3 --> HDFS3 end subgraph 特点2["💡 特点"] H1[列式存储NoSQL] H2[支持随机读写] H3[毫秒级延迟] H4[适合稀疏数据] end style HBase fill:#9b59b6
HBase vs HDFS vs 传统数据库:
| 特性 | HDFS | HBase | MySQL |
|---|---|---|---|
| 数据模型 | 文件 | 列族 | 表/行 |
| 读写模式 | 一次写多次读 | 随机读写 | 随机读写 |
| 延迟 | 高(秒级) | 低(毫秒级) | 低(毫秒级) |
| 数据量 | PB级 | PB级 | TB级 |
| 适用场景 | 批处理 | 实时查询 | OLTP |
Spark:内存计算的王者
RDD] SQL[Spark SQL
结构化数据] Streaming[Spark Streaming
流处理] MLlib[MLlib
机器学习] GraphX[GraphX
图计算] end style Spark fill:#ff6b6b
Spark vs MapReduce:
结论:Spark通过内存计算,避免了MapReduce频繁的磁盘IO,速度提升10-100倍。
Flink:流批一体的未来
作业管理] RM2[ResourceManager] Dispatcher[Dispatcher] end subgraph TMs["Task Managers"] TM1[TaskManager1] TM2[TaskManager2] TM3[TaskManager3] end JM --> TM1 JM --> TM2 JM --> TM3 end subgraph 特点3["💡 Flink特点"] F1[真正的流处理
事件驱动] F2[精确一次语义
Exactly-Once] F3[低延迟
毫秒级] F4[流批一体
统一API] end style Flink fill:#ff6b6b
流处理框架对比:
| 框架 | 处理模型 | 延迟 | 精确一次 | 适用场景 |
|---|---|---|---|---|
| Spark Streaming | 微批处理 | 秒级 | ✅ | 准实时 |
| Flink | 真正流处理 | 毫秒级 | ✅ | 实时 |
| Storm | 流处理 | 毫秒级 | ❌(需额外处理) | 简单实时 |
| Kafka Streams | 流处理 | 毫秒级 | ✅ | 轻量级 |
6.3 技术选型指南
七、一个完整的大数据架构案例
让我们看看一个真实的电商公司是怎么搭建大数据平台的:
业务数据库)] Third[第三方数据] end subgraph 采集层["📥 数据采集"] Flume2[Flume
日志采集] Kafka2[Kafka
消息队列] Sqoop2[Sqoop
数据库同步] Canal[Canal
Binlog采集] end subgraph 存储层["💾 数据存储"] subgraph 数据湖["数据湖"] HDFS4[HDFS
原始数据] end subgraph 数据仓库["数据仓库"] ODS[ODS层
原始数据] DWD[DWD层
明细数据] DWS[DWS层
汇总数据] ADS[ADS层
应用数据] end HBase2[HBase
实时查询] ES[Elasticsearch
搜索/日志] Redis[(Redis
缓存)] end subgraph 计算层["⚡ 数据计算"] Spark3[Spark
离线计算] Flink2[Flink
实时计算] Hive2[Hive
数仓查询] end subgraph 服务层["🖥️ 数据服务"] API[数据API] BI[BI报表] ML[机器学习平台] Search[搜索服务] end subgraph 调度监控["🔧 调度&监控"] Airflow2[Airflow
任务调度] Monitor[监控告警] end App --> Flume2 Web --> Flume2 DB --> Canal DB --> Sqoop2 Third --> Kafka2 Flume2 --> Kafka2 Canal --> Kafka2 Sqoop2 --> HDFS4 Kafka2 --> HDFS4 Kafka2 --> Flink2 Kafka2 --> HBase2 HDFS4 --> ODS ODS --> DWD DWD --> DWS DWS --> ADS Spark3 --> 数据仓库 Hive2 --> 数据仓库 Flink2 --> HBase2 Flink2 --> ES Flink2 --> Redis ADS --> API ADS --> BI HBase2 --> API ES --> Search Spark3 --> ML Airflow2 --> Spark3 Airflow2 --> Hive2 style HDFS4 fill:#4ecdc4 style Spark3 fill:#ff6b6b style Flink2 fill:#ff6b6b style Hive2 fill:#ffe66d
架构说明
| 层次 | 组件 | 作用 |
|---|---|---|
| 采集层 | Flume + Kafka + Canal | 收集各种数据源的数据 |
| 存储层 | HDFS + HBase + ES | 存储不同类型的数据 |
| 计算层 | Spark + Flink + Hive | 批处理和实时处理 |
| 服务层 | API + BI | 对外提供数据服务 |
| 调度层 | Airflow | 管理任务依赖和调度 |
八、Hadoop的未来:云原生时代
随着云计算的发展,Hadoop也在不断进化:
现代数据架构趋势
数据一致性难保证] end subgraph 现代架构["✅ 现代Lakehouse架构"] DL[Data Lakehouse
Delta Lake/Iceberg/Hudi] DL --> Batch[批处理] DL --> Stream[流处理] DL --> ML2[机器学习] DL --> BI2[BI分析] Note2[统一存储
流批一体
ACID事务] end style Note1 fill:#ff6b6b style Note2 fill:#4ecdc4
九、全系列总结
三篇文章,我们走完了Hadoop从入门到......还没放弃的旅程。让我们做一个全面的回顾:
学习路线建议
HDFS+MR+YARN] L3 --> L4[Hive数仓] L4 --> L5[Spark] L5 --> L6{方向选择} L6 --> |"离线方向"|L7[数仓建模
Hive优化] L6 --> |"实时方向"|L8[Flink
Kafka] L6 --> |"平台方向"|L9[调度系统
数据治理] L7 --> L10[数据架构师] L8 --> L10 L9 --> L10 end style L3 fill:#4ecdc4 style L5 fill:#ff6b6b style L10 fill:#ffe66d
十、写在最后
写到这里,Hadoop系列终于完结了。
回想起我刚开始学Hadoop的时候,面对一堆概念完全懵逼:NameNode、DataNode、JobTracker、TaskTracker、ResourceManager、NodeManager、ApplicationMaster......每个都是Manager,每个都是Node,到底谁管谁?
后来我发现,其实大数据技术的核心思想就那么几个:
- 分而治之:大任务拆小任务
- 数据冗余:用副本保证可靠性
- 移动计算:计算向数据靠拢
- 分层架构:存储、计算、调度分离
掌握了这些思想,再去学任何大数据组件都会轻松很多。
最后的最后,送给大家几句话:
"不要被技术的复杂性吓倒,所有复杂的系统都是由简单的模块组成的。"
"Hadoop可能会过时,但分布式计算的思想永远不会。"
"学习大数据最好的方式是:先跑起来,再深入原理。"
如果你真的认真看完了这三篇文章,恭喜你——你已经超过了80%的"大数据工程师"。
接下来,去动手搭个集群,跑几个任务,踩几个坑,你就真的入门了。
感谢阅读,我们下个系列见!🐘🐘🐘
本文作者:一个在凌晨三点调过YARN配置的程序员
最惨经历:调了一晚上参数,第二天发现是网线没插好
系列完结,撒花🎉
附录A:Hadoop生态圈速查表
| 组件 | 类型 | 一句话描述 |
|---|---|---|
| HDFS | 存储 | 分布式文件系统,存海量数据 |
| YARN | 调度 | 资源管理,给各种应用分配资源 |
| MapReduce | 计算 | 批处理计算框架,慢但稳 |
| Hive | 查询 | SQL on Hadoop,数仓首选 |
| HBase | 存储 | 列式NoSQL,支持随机读写 |
| Spark | 计算 | 内存计算,比MR快10-100倍 |
| Flink | 计算 | 流批一体,真正的实时 |
| Kafka | 消息 | 消息队列,数据管道 |
| Flume | 采集 | 日志采集工具 |
| Sqoop | 导入导出 | 关系数据库与Hadoop之间搬数据 |
| ZooKeeper | 协调 | 分布式协调服务,选主、配置管理 |
| Oozie | 调度 | 工作流调度,管理任务依赖 |
| Presto | 查询 | 交互式查询,秒级响应 |
| Impala | 查询 | MPP查询引擎,类似Presto |
附录B:面试高频题合集
YARN相关
YARN的架构是什么?各组件的作用?
RM负责全局资源管理,NM管理单节点资源,AM管理单个应用,Container是资源抽象。
YARN的作业提交流程?
客户端提交 → RM分配Container启动AM → AM向RM申请资源 → AM通知NM启动Container → 任务执行 → 完成释放资源
YARN有哪些调度器?区别是什么?
FIFO(先进先出)、Capacity(容量保证)、Fair(公平分配)。生产环境一般用Capacity或Fair。
Container和虚拟机/Docker的区别?
Container是YARN的资源抽象,不是真正的隔离容器。它只是逻辑上的资源限制(CPU、内存),不像Docker有文件系统隔离。
生态圈相关
Hive和传统数据库的区别?
Hive是数据仓库工具,基于HDFS,适合离线批处理,延迟高;传统数据库适合OLTP,低延迟。
HBase的rowkey设计原则?
唯一性、散列性(避免热点)、长度适中(10-100字节)、业务相关性。
Spark和MapReduce的区别?
Spark基于内存计算,支持丰富算子,适合迭代计算;MR中间结果写磁盘,只有Map和Reduce两个算子。
Flink和Spark Streaming的区别?
Flink是真正的流处理(事件驱动),Spark Streaming是微批处理。Flink延迟更低(毫秒级vs秒级)。