Hive on Spark 企业级调优(一):集群与引擎配置优化
你的 Hive 查询,还在用 MapReduce 跑吗?
如果你跟过我之前写的 Hive on MR 企业级调优 系列,应该对那种"调了半天参数,查询还是慢得像蜗牛"的无力感不陌生。MR 引擎的硬伤——每个 Stage 都要落盘、Shuffle 写一遍 HDFS、JVM 反复启停——不是靠调参能根治的。
换引擎,才是最大的调优。同样的 SQL 跑在 Hive on Spark 上,内存计算 + DAG 执行 + 更高效的 Shuffle,性能往往是数倍乃至一个数量级的提升——比如下面这条再普通不过的语句,换引擎前后的体感差距,可能比你调一个月参数还大:
SELECT dept, SUM(salary) FROM employee GROUP BY dept;
但"能跑"和"跑得好"之间,隔着一整套配置:
- Yarn 给多少内存、多少核?给多了浪费,给少了 OOM。
- 一个 Executor 分几核、多大内存?分错了要么资源碎片化,要么并行度上不去。
- Driver 给多少?动态分配怎么设?Shuffle 服务要不要开?
这些参数环环相扣,一个配错,整个集群的资源利用率可能只有 30%。
这篇文章,我会从集群规划 → Yarn 配置 → Spark 配置,一层一层把资源账算清楚。不堆参数,而是告诉你每个数字背后的推导逻辑,把抽象的数字转化为看得见的运行现场。看完之后,面对任何规格的集群,你都能自己算出一套合理的配置。
本文是 【Hive on Spark 企业级调优】 系列第 1 篇,后续还有 SQL 优化(上 / 下)两篇,关注不迷路
1. 集群搭建规划
(1)集群节点类型分为Master节点类型和Worker节点类型。Master节点用于部署主从架构集群中的Master服务(主要负责任务的调度),Worker节点主要用于部署主从架构中的Slave服务(负责执行具体的业务)。
(2)例如:Master节点常用于部署HDFS的NameNode,Yarn的ResourcManager等角色,Worker节点常用于部署HDFS的DataNode,Yarn的NodeManager等角色。
(3)Master节点的配置可以不需要太高,但是Worker节点的配置需要尽可能的高,因为需要执行具体的任务,对资源的依赖较高。
(4)下面基于以下配置进行讲解
- Master节点两台(主要是实现主从架构的高可用,排除单点故障),节点配置为16核CPU,64G内存
- Worker节点三台,节点配置为32核CPU,128内存
2. Yarn配置
需要调整的Yarn参数均与CPU,内存等资源相关,核心配置如下:
2.1 Yarn配置说明
(1)yarn.nodemanager.resource.memory-mb
该参数是用于定义一个NodeManager节点可以使用的内存大小(分配给container使用的内存),单位为MB。该参数的配置,取决于NodeManager所在节点(服务器)的总内存容量和该节点运行其他服务的数量。
NodeManager服务部署在Worker节点中,Worker节点的内存大小为128G,该节点还需要运行其他的服务,所以综合下来,可以设置该参数为64G。
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>65536</value>
</property>
补充:
# 如何判定参数是否给的合适,可以查看服务器运行时的剩余内存容量
free -h
关注 available 列而非 free 列,因为 Linux 会利用空闲内存做缓存。
(2)yarn.nodemanager.resource.cpu-vcores
该参数是用于定义一个NodeManager节点可以使用的CPU核数(分配给container使用)。该参数的配置同样需要考虑NodeManger所在节点的CPU核数和该节点运行的其他服务。
NodeManager服务部署在Worker节点中,Worker节点的CPU总核数32核,该节点还需要运行其他的服务。所以综合下来,可以分配一半的CPU核数给NodeManager节点,将参数设置为 16 核。
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>16</value>
</property>
(3)yarn.scheduler.maximum-allocation-mb
该参数用于定义单个container能够使用的最大内存。注意:由于Spark on Yarn模式下,Spark的 Driver 和 Executor 都运行在container中,故该参数不能小于 Driver 和 Executor 的内存配置。推荐配置为16G,既一个NodeManager最少可以同时启动 4 个container。
<property>
<name>yarn.scheduler.maximum-allocation-mb</name>
<value>16384</value>
</property>
(4)yarn.scheduler.minimum-allocation-mb
该参数含义:单个container能够使用的最小内存,推荐配置如下
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>512</value>
</property>
2.2 Yarn配置实操
(1)修改$HADOOP_HOME/etc/hadoop/yarn-site.xml文件
(2)修改如下参数
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>65536</value>
</property>
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>16</value>
</property>
<property>
<name>yarn.scheduler.maximum-allocation-mb</name>
<value>16384</value>
</property>
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>512</value>
</property>
(3)分发该配置文件
(4)重启yarn
Yarn集群的总资源大小
内存:64 * 3 = 192G
CPU:16 * 3 = 48核
3. Spark配置
3.1 Executor配置说明
3.1.1 spark.executor.cores
单个Executor的CPU核数,由spark.executor.cores参数决定,建议配置为 4-6,但是具体配置多少,还要视情况而定,原则就是充分的利用资源。
此处单个NodeManager节点共有16核CPU提供给Executor使用,则spark.executor.core的配置为4最合适。因为如果配置5个,则只能启动三个executor节点,浪费1个CPU。6个同理,启动2个节点,浪费4个CPU。
3.1.2 spark.executor.memory & spark.executor.memoryOverhead
Spark On Yarn模式下的 Executor 内存模型:

Executor内存相关的参数有:spark.executor.memoryOverhead 和 spark.executor.memory。
spark.executor.memory用于指定Executor进程的堆内存,用于任务的计算和存储。spark.executor.memoryOverhead用于指定Executor进程堆外内存,用于JVM的额外开销,操作系统的开销等。两者之和才是一个Executor进程所需的总内存大小。默认情况下:spark.executor.memory = spark.executor.memoryOverhead * 10。
以上两个参数推荐的配置思路:根据单个NodeManager的核数和单个Executor的核数,计算出每个NodeManager节点最多能够运行多少个Executor。再将NodeManager的总内存平均分配给每个Executor,最后将单个Executor的内存按照大概10:1的比例分配给spark.executor.memory和spark.executor.memoryOverhead。
根据以上思路:(spark.executor.memory + spark.executor.memoryOverhead)= 64G / 4 = 16G
综上所述:
spark.executor.memory 14G,
spark.executor.memoryOverhead 2G
3.1.3 Executor个数配置
Executor个数指分配给一个Spark应用的Executor个数,Executor的个数对于Spark应用的执行速度有很大的影响,如何能够合理的利用资源很重要。
一个spark应用的Executor个数指定方式用两种:**静态分配 **和 动态分配。
- 静态分配
可以通过 spark.executor.instances 参数指定一个spark应用启动的Executor个数。
缺点:这种方式需要自行估计每个spark应用所需要的资源,并为每个spark应用单独配置Executor个数。
- 动态分配
可以根据一个spark应用的工作负载,动态的调整Executor的个数。这意味着一个spark应用周期运行过程中,需要时可以申请更多的资源,不需要时可以释放空闲的资源。
在生产环境中,推荐使用动态分配。动态分配相关参数
# 启动动态分配
spark.dynamicAllocation.enabled true
# 启动 Spark Shuffle 服务
spark.shuffle.service.enabled true
# 设置Executor初始个数
spark.dynamicAllocation.initialExecutors 1
# 设置Executor最小个数
spark.dynamicAllocation.minExecutors 1
# 设置Executor最大个数 yarn集群总核数(48) / Executor核数(4)
spark.dynamicAllocation.maxExecutors 12
# Executor空闲时长,若某个Executor空闲时间超过这个值,就会关闭
spark.dynamicAllocation.executorIdleTimeout 60s
# 积压任务等待时长,若Task等待时间超过次值,则会申请新的Executor
spark.dynamicAllocation.schedulerBacklogTimeout 10s
关于第二个参数启动shuffle,shuffle服务的作用是管理Executor中的各Task输出文件,主要是shuffle过程中map端的输出文件。由于启动了动态分配,spark会在一个应用未结束前,将已经完成任务,超出空闲时间的Executor关闭。Executor关闭之后,其输出的文件没有办法共其他Executor使用。需要启动Spark Shuffle服务,来管理各Executor输出的文件,这样关闭空闲的Executor不会影响后续的计算任务。
3.2 Driver配置说明
Driver主要配置内存即可,相关的参数用spark.driver.memory 和 spark.driver.memoryOverhead。与Executor的内存配置类似。
spark.driver.memory 用于指定 Driver 进程堆内存大小。spark.driver.memoryOverhead 用于指定Driver进程的堆外内存大小,默认情况下: spark.driver.memory = spark.driver.memoryOverhead * 10,两者之和才是一个Driver进程的总内存大小。
假设yarn.nodemanager.resource.memory-mb 为 X,常规情况下:
X > 50G —— Driver可设置为12G
12G < X < 50G —— Driver可设置为 4G
1G < X < 12G —— Driver可设置为1G
此处 yarn.nodemanager.resource.memory-mb 为64G,则可以给Driver分配12G。
综上所述:
spark.driver.memory 10G
spark.driver.memoryOverhead 2G
3.3 Spark配置实操
(1)修改$HIVE_HOME/conf/spark-defaults.conf
# 运行模式
spark.master yarn
# 开启日志服务
spark.eventLog.enabled true
# 日志文件地址(HDFS)
spark.eventLog.dir hdfs://NameNode地址(active)/spark-history
# executor核数
spark.executor.cores 4
# executor 堆内存
spark.executor.memory 14g
# executor 堆外内存
spark.executor.memoryOverhead 2g
# driver 堆内存
spark.driver.memory 10g
# driver 堆外内存
spark.driver.memoryOverhead 2g
# 启动动态分配
spark.dynamicAllocation.enabled true
# 启动 Spark Shuffle 服务
spark.shuffle.service.enabled true
# 设置Executor初始个数
spark.dynamicAllocation.initialExecutors 1
# 设置Executor最小个数
spark.dynamicAllocation.minExecutors 1
# 设置Executor最大个数 yarn集群总核数(48) / Executor核数(4)
spark.dynamicAllocation.maxExecutors 12
# Executor空闲时长,若某个Executor空闲时间超过这个值,就会关闭
spark.dynamicAllocation.executorIdleTimeout 60s
# 积压任务等待时长,若Task等待时间超过次值,则会申请新的Executor
spark.dynamicAllocation.schedulerBacklogTimeout 10s
(2)配置 Spark Shuffle 服务
Spark Shuffle服务配置因Cluster Manager(standalone, Mesos, Yarn)的不同配置不一样。此处以Yan作为Cluster Manager。
(1)拷贝 $SPARK_HOME/yarn/spark-3.3.1-yarn-shuffle.jar 到 $HADOOP_HOME/share/hadoop/yarn/lib
(2)分发 $HADOOP_HOME/share/hadoop/yarn/lib/spark-3.3.1-yarn-shuffle.jar
(3)修改$HADOOP_HOME/etc/hadoop/yarn-site.xml 文件
<!-- 开启 Spark Shuffle 服务 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle,spark_shuffle</value>
</property>
<!-- 执行 Spark Shuffle 类名 -->
<property>
<name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
<value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>
(4)分发 $HADOOP_HOME/etc/hadoop/yarn-site.xml 配置文件
(5)重启 Yarn
3.4 Spark On Yarn总结
Spark on Yarn 的本质是:Spark 将自身的资源管理委托给 Yarn。Driver 负责"做什么"(DAG 调度),Yarn 的 ResourceManager + NodeManager 负责"在哪做"(Container 分配),Executor 负责"怎么做"(Task 执行)。三者通过 Container 这一抽象解耦,使得 Spark 无需自己管理集群资源,专注于计算逻辑。
3.4.1 Spark On Yarn 组件映射
| Spark 组件 | 运行位置 | 说明 |
|---|---|---|
| Driver | Cluster模式:Yarn 集群中的 Container Client模式:提交机器本地 | 负责创建 SparkContext、解析 DAG、调度 Task |
| ApplicationMaster (AM) | Yarn 集群中的 Container | 负责向 RM 申请资源、管理 Executor 生命周期 |
| Executor | Worker 节点的 Container 中 | 负责执行具体 Task、缓存 RDD 数据 |
| Task | Executor 内部的线程 | 实际计算单元(map/reduce 等) |
3.4.2 Spark On Yarn 流程

3.4.2 资源分配关系
Yarn 集群总资源
├── 内存:64G × 3 Worker = 192G
└── CPU :16核 × 3 Worker = 48核
单个 Worker 节点 (NodeManager) 可分配资源
├── 内存:64G(yarn.nodemanager.resource.memory-mb = 65536)
└── CPU :16核(yarn.nodemanager.resource.cpu-vcores = 16)
单个 Executor 占用资源
├── CPU :4核(spark.executor.cores = 4)
├── 堆内存:14G(spark.executor.memory)
└── 堆外内存:2G(spark.executor.memoryOverhead)
→ 合计 16G = 单个 Container 最大内存(yarn.scheduler.maximum-allocation-mb)
每个 Worker 可运行 Executor 数
├── 按 CPU:16核 ÷ 4核 = 4 个
└── 按内存:64G ÷ 16G = 4 个
→ 每个 Worker 最多 4 个 Executor
集群最大 Executor 数(动态分配上限)
└── 48核 ÷ 4核 = 12 个(spark.dynamicAllocation.maxExecutors = 12)
Driver 占用资源(独占一个 Container)
├── 堆内存:10G
└── 堆外内存:2G
→ 合计 12G
更多推荐
所有评论(0)