动态资源分配开了以后资源一直不释放——怎么回事?
先说结论
动态资源分配的核心逻辑:Executor空闲超过一定时间才会被释放。不释放的常见原因是:Executor上有缓存数据(cache)或有Shuffle数据未被读取,Spark不会释放这些Executor。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
Spark的动态资源分配(Dynamic Resource Allocation)看起来很美好——作业忙的时候自动加Executor,闲的时候自动释放。但生产环境里经常遇到“开了但资源不释放”的问题。今天把原理、坑和正确配置讲清楚。
动态资源分配的工作原理
1. 有待处理的Task时,申请新的Executor
2. Executor空闲超过executorIdleTimeout(默认60s)后被移除
3. 但有两种Executor不会被移除:
a. 存有缓存数据的Executor —— 因为释放了就丢了缓存,下次要重算
b. 存有Shuffle数据的Executor —— 因为下游Task可能还要来拉取这些Shuffle数据
三个常见的坑
坑一:cache没unpersist导致Executor永远不释放
你在作业里cache了一个DataFrame但没有unpersist,这些缓存数据分布在多个Executor上。动态资源分配认为这些Executor“有数据”,不会释放。
解决: 用完unpersist()。或者设置cachedExecutorIdleTimeout让有缓存的Executor也能被释放:
// 设置有缓存的Executor的空闲超时(默认是永不释放!)
spark.conf.set("spark.dynamicAllocation.cachedExecutorIdleTimeout", "600s")
// 10分钟空闲后释放,即使有缓存
坑二:Shuffle数据导致Executor不释放
Spark 3.0之前,Shuffle数据存在Executor的本地磁盘上,释放Executor就丢了Shuffle数据,下游Task会失败。所以有Shuffle数据的Executor不会被释放。
Spark 3.0+的解决方案:External Shuffle Service
// 开启外部Shuffle服务,Shuffle数据由NodeManager管理而不是Executor
spark.conf.set("spark.shuffle.service.enabled", "true")
// 这样即使Executor被释放,Shuffle数据仍然可读
在Databricks里这个是默认开启的,不需要手动配置。但在自建YARN集群上需要手动开启。
坑三:和推测执行的冲突
动态资源分配和推测执行(Speculative Execution)同时开启可能有冲突。推测执行会为慢Task启动备份,而动态分配可能刚好把这个Executor释放了。建议二选一,一般开动态分配关推测执行。
实战配置模板
// 生产环境推荐配置
spark.conf.set("spark.dynamicAllocation.enabled", "true")
spark.conf.set("spark.dynamicAllocation.minExecutors", "2")
spark.conf.set("spark.dynamicAllocation.maxExecutors", "50")
spark.conf.set("spark.dynamicAllocation.executorIdleTimeout", "60s")
spark.conf.set("spark.dynamicAllocation.cachedExecutorIdleTimeout", "600s")
spark.conf.set("spark.shuffle.service.enabled", "true")
spark.conf.set("spark.speculation", "false") // 关推测执行
总结
动态资源分配不释放的常见原因:cache未unpersist、Shuffle数据未用External Shuffle Service。配置时要设置cachedExecutorIdleTimeout、开启外部Shuffle服务、关推测执行。面试时能说清楚“为什么不释放”的根因,而不只是“加了个配置”,就很有深度。
下一篇聊一个越来越重要的话题:Spark怎么和Delta Lake/Iceberg配合?数据湖和Spark的关系是什么?
你们生产环境用动态资源分配吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长