Spark Driver 内存参数你真的知道怎么设置么
Spark Driver 内存参数详解
📊 参数概览
你的配置:
spark.driver.memory=1g
spark.driver.memoryOverhead=1g
YARN Container 总内存 = 1g + 1g = 2g
1. 💾 spark.driver.memory = 1g
基本含义
Driver 的 JVM 堆内存(Heap Memory)大小
Driver 是什么?
Driver(驱动程序/Application Master):
├─ Spark 应用的"大脑"和"指挥中心"
├─ 负责协调整个任务执行
└─ 运行位置:
├─ Client 模式:本地客户端机器
└─ Cluster 模式:YARN 集群中的某个节点 ⭐(常用)
Driver 内存结构(1g 堆内存)
┌─────────────────────────────────────────────────────┐
│ Driver JVM Heap (1 GB) │
├─────────────────────────────────────────────────────┤
│ │
│ ① Spark 内部结构:~400 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ DAG Scheduler(作业调度) │ │
│ │ ├─ Stage 划分 │ │
│ │ ├─ Task 分配 │ │
│ │ └─ 依赖关系管理 │ │
│ │ │ │
│ │ Task Scheduler(任务调度) │ │
│ │ ├─ Task 队列 │ │
│ │ ├─ Executor 状态跟踪 │ │
│ │ └─ 任务重试逻辑 │ │
│ │ │ │
│ │ Block Manager Master(存储管理) │ │
│ │ ├─ RDD/DataFrame 缓存元数据 │ │
│ │ ├─ 广播变量跟踪 │ │
│ │ └─ Shuffle 文件位置 │ │
│ │ │ │
│ │ Broadcast Manager(广播变量管理) │ │
│ │ ├─ 广播变量分发 │ │
│ │ └─ 广播变量内容(如果有) │ │
│ │ │ │
│ │ Metrics System(监控指标) │ │
│ │ ├─ 任务指标收集 │ │
│ │ └─ 资源使用统计 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ② Task 元数据:~200 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ 每个 Task 的元数据:~1 KB │ │
│ │ ├─ Task ID │ │
│ │ ├─ Partition 信息 │ │
│ │ ├─ 序列化的 Task 代码 │ │
│ │ ├─ 依赖信息 │ │
│ │ └─ Executor 分配信息 │ │
│ │ │ │
│ │ 示例计算: │ │
│ │ 200,000 Tasks × 1 KB = 200 MB │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ③ 用户代码和数据:~300 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ collect() 结果 │ │
│ │ ├─ df.collect() → 拉取数据到 Driver │ │
│ │ └─ 限制:spark.driver.maxResultSize │ │
│ │ │ │
│ │ 广播变量内容 │ │
│ │ ├─ spark.broadcast(data) │ │
│ │ └─ 小表数据(用于 Broadcast Join) │ │
│ │ │ │
│ │ UDF/代码定义 │ │
│ │ ├─ 自定义函数 │ │
│ │ └─ 第三方库 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ④ 缓冲区:~100 MB │
│ ├─ RPC 通信缓冲 │
│ ├─ 事件队列 │
│ └─ 临时对象 │
└─────────────────────────────────────────────────────┘
总计:400 + 200 + 300 + 100 = 1000 MB = 1 GB ✅
Driver 做什么(需要内存的操作)
操作 1:任务调度和编排
Hive SQL 提交:
SELECT col1, COUNT(*)
FROM large_table
WHERE date='2025-11-28'
GROUP BY col1;
Driver 处理:
┌─────────────────────────────────────────┐
│ Step 1: 解析 SQL │
│ ├─ Hive → Spark Logical Plan │
│ └─ 内存占用:~10 MB │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 2: 优化(CBO) │
│ ├─ 基于统计信息优化 │
│ ├─ 生成 Physical Plan │
│ └─ 内存占用:~20 MB │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 3: 划分 Stage │
│ ├─ 根据 Shuffle 边界划分 │
│ ├─ Stage 1: Scan + Filter │
│ ├─ Stage 2: Shuffle + Aggregate │
│ └─ 内存占用:~30 MB │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 4: 创建 Tasks │
│ ├─ 根据 Partitions 创建 Tasks │
│ ├─ 假设 1000 个分区 = 1000 Tasks │
│ ├─ 每 Task 元数据:1 KB │
│ └─ 内存占用:1000 × 1 KB = 1 MB ✅ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 5: 调度到 Executors │
│ ├─ 维护 Task 队列 │
│ ├─ 跟踪 Executor 状态 │
│ ├─ 处理 Task 完成/失败 │
│ └─ 内存占用:~50 MB │
└─────────────────────────────────────────┘
总计:10 + 20 + 30 + 1 + 50 = 111 MB
操作 2:收集结果(危险操作)⚠️
-- ❌ 危险操作:collect() 大量数据
val result = spark.sql("""
SELECT * FROM large_table
WHERE date='2025-11-28'
""").collect() -- 拉取 5 GB 数据到 Driver ❌
内存使用:
Driver Heap (1 GB):
├─ Spark 内部:400 MB
├─ Task 元数据:100 MB
├─ 结果数据:5 GB ❌❌❌ 超出限制!
└─ → java.lang.OutOfMemoryError: Java heap space
-- ✅ 正确做法 1:限制数据量
val result = spark.sql("""
SELECT * FROM large_table
WHERE date='2025-11-28'
LIMIT 1000
""").collect() -- 只拉取 1000 行 ✅
内存使用:~10 MB ✅
-- ✅ 正确做法 2:不拉取到 Driver
spark.sql("""
SELECT * FROM large_table
WHERE date='2025-11-28'
""").write.parquet("/output/path") -- 直接写入 HDFS ✅
Driver 内存:不受影响 ✅
操作 3:广播变量(小心使用)⚠️
-- 场景:Broadcast Join
-- 小表:100 MB
-- 大表:10 GB
SELECT a.*, b.*
FROM large_table a
JOIN small_table b ON a.id = b.id;
Spark 自动广播逻辑(如果 small_table < 10 MB):
┌─────────────────────────────────────────┐
│ Step 1: Driver 收集小表数据 │
│ ├─ 从 Executors 收集 small_table │
│ ├─ 数据汇总到 Driver │
│ └─ Driver 内存:100 MB ⚠️ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 2: Driver 创建广播变量 │
│ ├─ spark.broadcast(small_table) │
│ ├─ 序列化数据 │
│ └─ Driver 内存:100 MB × 1.5 = 150 MB │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 3: 分发到所有 Executors │
│ ├─ 通过网络发送 │
│ ├─ 每个 Executor 接收一份 │
│ └─ Driver 内存:仍占用 150 MB │
└─────────────────────────────────────────┘
如果 small_table = 800 MB:
Driver Heap (1 GB):
├─ Spark 内部:400 MB
├─ 广播变量:800 MB × 1.5 = 1.2 GB ❌❌
└─ → Driver OOM!
解决方案:
-- 增大 driver.memory
SET spark.driver.memory=4g;
-- 或禁用自动广播
SET spark.sql.autoBroadcastJoinThreshold=-1;
操作 4:维护 Task 元数据
Task 数量的影响:
场景 1:合理的 Task 数量
├─ Shuffle Partitions:200
├─ Stages:3
├─ 总 Tasks:200 × 3 = 600
├─ Task 元数据:600 × 1 KB = 600 KB ✅
└─ Driver 内存影响:极小
场景 2:过多的 Task
├─ Shuffle Partitions:100,000 ❌
├─ Stages:5
├─ 总 Tasks:100,000 × 5 = 500,000
├─ Task 元数据:500,000 × 1 KB = 500 MB ⚠️
└─ Driver 内存压力:明显增加
场景 3:极端情况
├─ Shuffle Partitions:1,000,000 ❌❌
├─ Stages:10
├─ 总 Tasks:10,000,000
├─ Task 元数据:10,000,000 × 1 KB = 10 GB ❌❌❌
└─ → Driver OOM!
推荐:
spark.sql.shuffle.partitions=200-2000 ✅
不要设置过大(如 100,000)
1g Driver Memory 够用吗?
✅ 够用的场景
1. 简单查询
├─ 单表查询或 2-3 表 Join
├─ Task 数量 < 10,000
├─ 没有 collect() 或很少
└─ 没有大的广播变量(< 100 MB)
2. 标准 ETL
├─ INSERT INTO ... SELECT ...
├─ 直接写入 HDFS/Hive
├─ 不拉数据到 Driver
└─ Task 数量适中
3. 轻量级任务
├─ Spark Shuffle Partitions < 1000
├─ 数据量 < 100 GB
└─ Executors < 50 个
❌ 不够用的场景
1. 大量 collect()
├─ df.collect() 返回大量数据
└─ 需要:2-8 GB
2. 大广播变量
├─ 广播表 > 500 MB
└─ 需要:4-8 GB
3. 大量 Tasks
├─ Shuffle Partitions > 50,000
├─ 或复杂的 DAG(10+ Stages)
└─ 需要:4-8 GB
4. 交互式查询(Spark Shell)
├─ 多次 collect()
├─ 临时对象累积
└─ 需要:2-4 GB
5. 复杂统计分析
├─ CBO 优化大量表(10+ 表 Join)
├─ 复杂的执行计划
└─ 需要:2-4 GB
2. 🔧 spark.driver.memoryOverhead = 1g
基本含义
Driver 的堆外内存(Off-Heap/Native Memory)大小
Driver memoryOverhead 的组成
┌─────────────────────────────────────────────────────┐
│ Driver memoryOverhead (1 GB) │
├─────────────────────────────────────────────────────┤
│ │
│ ① JVM Metaspace(元空间):~256 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ 类元数据 │ │
│ │ ├─ Spark 核心类 │ │
│ │ ├─ Hive 相关类 │ │
│ │ ├─ JDBC 驱动 │ │
│ │ └─ 用户 UDF 类 │ │
│ │ │ │
│ │ JVM 配置: │ │
│ │ -XX:MetaspaceSize=256m │ │
│ │ -XX:MaxMetaspaceSize=512m │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ② 线程栈(Thread Stacks):~50 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ Driver 线程: │ │
│ │ ├─ DAG Scheduler 线程 │ │
│ │ ├─ Task Scheduler 线程 │ │
│ │ ├─ RPC 通信线程(Netty) │ │
│ │ ├─ 心跳线程 │ │
│ │ ├─ 事件处理线程 │ │
│ │ └─ 其他守护线程 │ │
│ │ │ │
│ │ 线程数:~20-30 个 │ │
│ │ 每线程栈:1 MB │ │
│ │ 总计:30 × 1 MB = 30 MB │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ③ RPC 通信缓冲区(Netty):~300 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ Driver ↔ Executor 通信 │ │
│ │ ├─ Task 提交 │ │
│ │ ├─ Task 结果接收 │ │
│ │ ├─ 心跳接收 │ │
│ │ ├─ Shuffle 元数据 │ │
│ │ └─ 广播变量分发 │ │
│ │ │ │
│ │ Netty 缓冲区: │ │
│ │ ├─ 连接池:50 Executors × 2 MB = 100 MB │ │
│ │ ├─ 发送缓冲:64 KB × 50 = 3.2 MB │ │
│ │ ├─ 接收缓冲:64 KB × 50 = 3.2 MB │ │
│ │ └─ Netty 内存池:200 MB │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ④ Direct ByteBuffer(直接内存):~200 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ NIO 操作 │ │
│ │ ├─ HDFS 文件读写 │ │
│ │ ├─ 序列化/反序列化缓冲区 │ │
│ │ └─ 网络 I/O │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ⑤ Native 库内存:~50 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ Hadoop Native 库 │ │
│ │ 压缩库(Snappy/LZ4) │ │
│ │ JNI 调用 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ⑥ Code Cache(代码缓存):~100 MB │
│ ┌─────────────────────────────────────────────┐ │
│ │ JIT 编译后的机器码 │ │
│ │ -XX:ReservedCodeCacheSize=128m │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ⑦ 其他 JVM 开销:~44 MB │
│ ├─ GC 元数据 │
│ ├─ 符号表 │
│ └─ 内部数据结构 │
└─────────────────────────────────────────────────────┘
总计:256 + 50 + 300 + 200 + 50 + 100 + 44 = 1000 MB = 1 GB ✅
Driver memoryOverhead 的作用
作用 1:RPC 通信(Driver ↔ Executors)
通信场景:
1. Task 提交
Driver → Executor:
├─ 序列化的 Task 对象(代码 + 闭包)
├─ 平均大小:10-100 KB per Task
└─ 50 个并发 Executors × 50 KB = 2.5 MB ✅
2. Task 结果接收
Executor → Driver:
├─ Task 执行状态
├─ Metrics(执行时间、内存使用等)
├─ 平均大小:1 KB per Task
└─ 持续接收,缓冲区:50 MB ✅
3. 心跳
Executor → Driver:
├─ 每 10 秒一次
├─ 大小:~1 KB
└─ 50 Executors × 1 KB = 50 KB ✅
4. Shuffle 元数据
Executor → Driver:
├─ Shuffle 文件位置
├─ 大小:100 bytes per file
├─ 10,000 Shuffle 文件 × 100 B = 1 MB ✅
└─ 缓冲区:10 MB ✅
Netty 缓冲池需求:
= 发送 + 接收 + 池化 + 元数据
= 50 + 50 + 150 + 50
= 300 MB ✅ 正常需求
作用 2:广播变量分发
场景:广播 500 MB 小表
Driver 操作:
┌─────────────────────────────────────────┐
│ Step 1: 序列化广播数据 │
│ ├─ 堆内存(driver.memory):500 MB │
│ ├─ 堆外临时缓冲:500 MB × 0.3 = 150 MB │
│ └─ memoryOverhead 占用:150 MB ⚠️ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Step 2: 分块传输(通过 Netty) │
│ ├─ 块大小:4 MB │
│ ├─ 块数:500 MB / 4 MB = 125 块 │
│ ├─ 并发发送:10 个连接 │
│ ├─ 发送缓冲:4 MB × 10 = 40 MB │
│ └─ memoryOverhead 占用:40 MB ⚠️ │
└─────────────────────────────────────────┘
总 memoryOverhead 需求:
= 序列化缓冲 + 发送缓冲
= 150 + 40 = 190 MB
如果 memoryOverhead 只有 100 MB:
→ 可能导致广播失败或很慢 ❌
作用 3:Metaspace 类加载
Driver 加载的类:
1. Spark 核心类:~150 MB
├─ org.apache.spark.*
├─ scala.*
└─ java.*
2. Hive 相关类:~50 MB
├─ org.apache.hadoop.hive.*
├─ org.apache.hive.*
└─ Hive UDF
3. 用户代码和 UDF:~30 MB
├─ 自定义 UDF
└─ 第三方依赖(JSON、HTTP 等)
4. JDBC 驱动(如果有):~20 MB
├─ MySQL Connector
├─ PostgreSQL Driver
└─ 其他数据库驱动
5. 其他库:~6 MB
总计:150 + 50 + 30 + 20 + 6 = 256 MB
JVM 配置:
-XX:MetaspaceSize=256m # 初始大小
-XX:MaxMetaspaceSize=512m # 最大大小
如果 Metaspace 不够:
→ java.lang.OutOfMemoryError: Metaspace
1g memoryOverhead 够用吗?
✅ 够用的场景
1. 小规模集群
├─ Executors < 50 个
├─ 并发 Tasks < 500
└─ 广播变量 < 100 MB
2. 标准 Hive on Spark 查询
├─ 纯 SQL 查询
├─ 没有复杂 UDF
└─ 没有大广播变量
3. 批处理 ETL
├─ 顺序执行
├─ RPC 通信量适中
└─ 1g overhead 足够
⚠️ 可能不够的场景
1. 大规模集群
├─ Executors > 100 个
├─ RPC 连接池需求:200 MB
├─ 建议:2g memoryOverhead
2. 大广播变量
├─ 广播表 > 500 MB
├─ 序列化和传输开销大
├─ 建议:2-3g memoryOverhead
3. 复杂应用
├─ 大量自定义 UDF
├─ 多个第三方依赖
├─ Metaspace 需求 > 256 MB
├─ 建议:1.5-2g memoryOverhead
4. 高并发场景
├─ 同时提交大量 Tasks
├─ RPC 缓冲区压力大
├─ 建议:2g memoryOverhead
3. 💡 YARN Container 总内存
YARN 为 Driver 分配的 Container:
= spark.driver.memory + spark.driver.memoryOverhead
= 1g + 1g
= 2g
┌─────────────────────────────────────┐
│ YARN Container: 2 GB │
├─────────────────────────────────────┤
│ JVM Heap: 1 GB │
│ ├─ Spark 内部:400 MB │
│ ├─ Task 元数据:200 MB │
│ ├─ 用户数据:300 MB │
│ └─ 缓冲区:100 MB │
├─────────────────────────────────────┤
│ Off-Heap: 1 GB │
│ ├─ Metaspace:256 MB │
│ ├─ Thread Stacks:50 MB │
│ ├─ RPC (Netty):300 MB │
│ ├─ Direct Buffers:200 MB │
│ ├─ Native Libs:50 MB │
│ ├─ Code Cache:100 MB │
│ └─ 其他:44 MB │
└─────────────────────────────────────┘
YARN 监控:
if (实际使用 > 2 GB):
杀死 Driver Container ❌
报错:Container killed by YARN for exceeding physical memory limits
4. 🔍 常见问题和解决方案
问题 1:Driver OOM(堆内存不足)
症状
# 错误日志
java.lang.OutOfMemoryError: Java heap space
at scala.collection.mutable.ListBuffer.$$plus$$plus$$eq
# Spark UI
Driver 显示失败或无响应
原因分析
1. collect() 太多数据
df.collect() -- 拉取 5 GB ❌
2. 广播变量太大
spark.broadcast(bigTable) -- 1 GB 表 ❌
3. Task 数量过多
spark.sql.shuffle.partitions=500000 -- 50 万 Tasks ❌
Task 元数据:500,000 × 1 KB = 500 MB
4. 内存泄漏
重复执行查询,对象未释放
解决方案
-- 方案 1:增大 driver.memory ✅
SET spark.driver.memory=4g; -- 从 1g 增大
-- 方案 2:限制结果大小
SET spark.driver.maxResultSize=2g; -- 限制 collect() 大小
-- 方案 3:避免 collect()
-- ❌ val data = df.collect()
-- ✅ df.write.parquet("/output")
-- 方案 4:减少广播
SET spark.sql.autoBroadcastJoinThreshold=10485760; -- 10 MB
-- 或禁用
SET spark.sql.autoBroadcastJoinThreshold=-1;
-- 方案 5:减少 Task 数量
SET spark.sql.shuffle.partitions=200; -- 从 500000 减少
问题 2:Driver 被 YARN 杀死
症状
# YARN 日志
Container killed by YARN for exceeding physical memory limits.
2.1 GB of 2 GB physical memory used.
Consider boosting spark.driver.memoryOverhead.
原因分析
实际内存使用:
├─ driver.memory: 1 GB
├─ memoryOverhead (实际): 1.1 GB
├─ 总计:2.1 GB
YARN 限制:
├─ driver.memory + memoryOverhead = 2 GB
├─ 实际使用 2.1 GB > 2 GB ❌
└─ 被杀死
解决方案
-- 方案 1:增大 memoryOverhead ✅
SET spark.driver.memory=1g;
SET spark.driver.memoryOverhead=1.5g; -- 从 1g 增大
-- 总计:2.5g
-- 方案 2:重新平衡
SET spark.driver.memory=1.5g;
SET spark.driver.memoryOverhead=1.5g;
-- 总计:3g
-- 方案 3:减少 RPC 压力
SET spark.executor.instances=20; -- 从 100 减少
-- 减少 Driver ↔ Executor 连接数
问题 3:Metaspace OOM
症状
java.lang.OutOfMemoryError: Metaspace
原因
1. 大量 UDF 类
2. 频繁类加载/卸载
3. 第三方库过多
解决方案
-- 方案 1:增大 memoryOverhead
SET spark.driver.memoryOverhead=2g; -- 为 Metaspace 预留更多
-- 方案 2:JVM 参数(在 spark-defaults.conf)
spark.driver.extraJavaOptions=-XX:MaxMetaspaceSize=512m
-- 方案 3:减少依赖
-- 精简第三方库,只打包必需的
问题 4:RPC 超时
症状
org.apache.spark.rpc.RpcTimeoutException:
Futures timed out after [120 seconds]
原因
Driver ↔ Executor 通信缓冲区不足
或网络拥塞
解决方案
-- 方案 1:增大 memoryOverhead(增加缓冲区)
SET spark.driver.memoryOverhead=2g;
-- 方案 2:增加 RPC 超时时间
SET spark.rpc.askTimeout=300s; -- 从 120s 增大
SET spark.network.timeout=300s;
-- 方案 3:减少并发度
SET spark.executor.instances=30; -- 减少 Executor 数量
-- 减轻 Driver RPC 压力
5. 📊 配置建议
场景 1:小规模 Hive 查询(你的配置)
-- 适用条件:
-- ✅ 数据量 < 100 GB
-- ✅ Executors < 20 个
-- ✅ 简单 SQL,少 collect()
-- ✅ 广播变量 < 100 MB
-- 配置
SET spark.driver.memory=1g; ✅ 够用
SET spark.driver.memoryOverhead=1g; ✅ 够用
-- 总内存:2g
场景 2:中等规模生产查询(推荐)
-- 适用条件:
-- ✅ 数据量 100 GB - 1 TB
-- ✅ Executors 20-100 个
-- ✅ 复杂 SQL,偶尔 collect()
-- ✅ 广播变量 < 500 MB
-- 配置
SET spark.driver.memory=2g; ⭐ 增大
SET spark.driver.memoryOverhead=2g; ⭐ 增大
-- 总内存:4g ✅ 更稳定
场景 3:大规模 ETL
-- 适用条件:
-- ✅ 数据量 > 1 TB
-- ✅ Executors > 100 个
-- ✅ 复杂 DAG(10+ Stages)
-- ✅ 大广播变量(> 500 MB)
-- 配置
SET spark.driver.memory=4g; ⭐⭐ 大幅增加
SET spark.driver.memoryOverhead=4g; ⭐⭐ 大幅增加
-- 总内存:8g ✅ 充裕
场景 4:交互式查询(Spark Shell/Notebook)
-- 适用条件:
-- ✅ 频繁 collect()
-- ✅ 多次查询,临时对象累积
-- ✅ 实验性查询,不确定数据量
-- 配置
SET spark.driver.memory=4g; ⭐ 预留更多
SET spark.driver.memoryOverhead=2g;
SET spark.driver.maxResultSize=2g; ⭐ 限制结果大小
-- 总内存:6g ✅
场景 5:超大广播 Join
-- 适用条件:
-- ✅ 广播表 > 1 GB
-- ✅ 多个广播变量
-- 配置
SET spark.driver.memory=8g; ⭐⭐⭐ 必须大
SET spark.driver.memoryOverhead=4g;
SET spark.sql.autoBroadcastJoinThreshold=1073741824; -- 1 GB
-- 总内存:12g ✅
-- 或者考虑不广播
SET spark.sql.autoBroadcastJoinThreshold=-1; -- 禁用
-- 使用 Shuffle Join 代替
6. 🎯 快速决策表
| 场景 | driver.memory | memoryOverhead | 总内存 | 适用条件 |
|---|---|---|---|---|
| 小规模查询 | 1g | 1g | 2g | < 100 GB,< 20 Executors |
| 中等生产 | 2g | 2g | 4g | 100 GB - 1 TB,20-100 Executors |
| 大规模 ETL | 4g | 4g | 8g | > 1 TB,> 100 Executors |
| 交互式 | 4g | 2g | 6g | 频繁 collect(),实验性查询 |
| 大广播 Join | 8g | 4g | 12g | 广播表 > 1 GB |
7. 📋 检查清单
检查 1:监控 Driver 内存使用
# 方法 1:Spark UI
http://<driver>:4040/environment/
# 查看:
- spark.driver.memory
- spark.driver.memoryOverhead
http://<driver>:4040/executors/
# Driver 行查看:
- Storage Memory
- Used Heap Memory
# 方法 2:YARN UI
http://<rm>:8088/cluster/app/<app_id>
# 查看 Driver Container 的内存使用
检查 2:常见错误信号
# 信号 1:Driver OOM
grep "OutOfMemoryError" driver.log
→ 增大 driver.memory
# 信号 2:Container 被杀
grep "Container killed by YARN" driver.log
→ 增大 memoryOverhead
# 信号 3:RPC 超时
grep "RpcTimeoutException" driver.log
→ 增大 memoryOverhead 或减少 Executors
# 信号 4:Metaspace 不足
grep "OutOfMemoryError: Metaspace" driver.log
→ 增大 memoryOverhead
检查 3:优化建议
-- 1. 限制结果大小
SET spark.driver.maxResultSize=2g;
-- 2. 控制广播阈值
SET spark.sql.autoBroadcastJoinThreshold=10485760; -- 10 MB
-- 3. 合理的 Task 数量
SET spark.sql.shuffle.partitions=200; -- 不要太大
-- 4. 避免大 collect()
-- 使用 limit、write 代替 collect()
-- 5. 监控内存使用
-- 定期查看 Spark UI 和 YARN UI
8. 🎓 总结
配置解读
spark.driver.memory=1g
→ Driver JVM 堆内存 1 GB
→ 存储:Spark 内部结构、Task 元数据、用户数据
spark.driver.memoryOverhead=1g
→ Driver 堆外内存 1 GB
→ 存储:Metaspace、RPC 缓冲区、线程栈等
YARN Container 总内存 = 1g + 1g = 2g
→ YARN 为 Driver 分配 2 GB 物理内存
核心要点
1. Driver Memory 主要用途:
├─ 任务调度(DAG、Task Scheduler)
├─ 元数据管理(Task、Partition、Executor 状态)
├─ collect() 结果(⚠️ 危险)
└─ 广播变量(⚠️ 注意大小)
2. Driver memoryOverhead 主要用途:
├─ Metaspace(类加载)
├─ RPC 通信(Driver ↔ Executors)
├─ 线程栈
└─ 直接内存(NIO)
3. 配置原则:
├─ 小规模:1g + 1g = 2g ✅
├─ 中规模:2g + 2g = 4g ✅ 推荐
├─ 大规模:4g + 4g = 8g ✅
└─ 超大:8g + 4g = 12g ✅
4. 常见陷阱:
❌ collect() 大量数据
❌ 广播变量过大
❌ Task 数量过多
❌ 忘记设置 memoryOverhead
立即行动
-- 如果你的查询经常失败或变慢:
-- 方案 1:保守增大(推荐)
SET spark.driver.memory=2g;
SET spark.driver.memoryOverhead=2g;
-- 方案 2:添加保护措施
SET spark.driver.maxResultSize=2g; -- 防止 collect() 过大
SET spark.sql.autoBroadcastJoinThreshold=10485760; -- 限制广播
-- 方案 3:监控验证
-- 运行查询后检查 Spark UI
-- Driver 内存使用是否接近上限
你的 1g + 1g 配置对于小到中等规模的 Hive 查询是合理的,但如果遇到 Driver OOM 或 Container 被杀的问题,建议升级到 2g + 2g 配置!✅
4251

被折叠的 条评论
为什么被折叠?



