Spark3.1.2 standalone集群s3a目录提交器OOM无法创建线程问题求助
Spark 3.1.2启用s3a directory committer后Driver OOM(线程数超限制)问题排查
问题背景
使用Spark 3.1.2 standalone集群,启用s3a directory committer后作业稳定性和性能有明显提升,但近期频繁出现Driver OOM错误,报错为无法创建原生线程。
错误日志
An error occurred while calling None.org.apache.spark.api.java.JavaSparkContext. : java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:803) at java.base/java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:937) at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1343) at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118) at java.base/java.util.concurrent.Executors$DelegatedExecutorService.submit(Executors.java:714) at org.apache.spark.rpc.netty.DedicatedMessageLoop.$anonfun$new$1(MessageLoop.scala:174) at org.apache.spark.rpc.netty.DedicatedMessageLoop.$anonfun$new$1$adapted(MessageLoop.scala:173) at scala.collection.immutable.Range.foreach(Range.scala:158) at org.apache.spark.rpc.netty.DedicatedMessageLoop.<init>(MessageLoop.scala:173) at org.apache.spark.rpc.netty.Dispatcher.liftedTree1$1(Dispatcher.scala:75) at org.apache.spark.rpc.netty.Dispatcher.registerRpcEndpoint(Dispatcher.scala:72) at org.apache.spark.rpc.netty.NettyRpcEnv.setupEndpoint(NettyRpcEnv.scala:136) at org.apache.spark.storage.BlockManager.<init>(BlockManager.scala:231) at org.apache.spark.SparkEnv$.create(SparkEnv.scala:394) at org.apache.spark.SparkEnv$.createDriverEnv(SparkEnv.scala:189) at org.apache.spark.SparkContext.createSparkEnv(SparkContext.scala:277) at org.apache.spark.SparkContext.<init>(SparkContext.scala:458) at org.apache.spark.api.java.JavaSparkContext.<init>(JavaSparkContext.scala:58) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:247) at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357) at py4j.Gateway.invoke(Gateway.java:238) at py4j.commands.ConstructorCommand.invokeConstructor(ConstructorCommand.java:80) at py4j.commands.ConstructorCommand.execute(ConstructorCommand.java:69) at py4j.GatewayConnection.run(GatewayConnection.java:238) at java.base/java.lang.Thread.run(Thread.java:834)
排查信息
- 线程栈Dump显示Driver上存在超过5000个处于WAITING状态的
s3-committer-pool线程,远超配置的线程数上限,部分线程信息如下:
Thread ID Thread Name Thread State Thread Locks 1047 s3-committer-pool-0 WAITING 1449 s3-committer-pool-0 WAITING 1468 s3-committer-pool-0 WAITING 1485 s3-committer-pool-0 WAITING 1505 s3-committer-pool-0 WAITING 1524 s3-committer-pool-0 WAITING 1529 s3-committer-pool-0 WAITING 1544 s3-committer-pool-0 WAITING 1549 s3-committer-pool-0 WAITING 1809 s3-committer-pool-0 WAITING 1972 s3-committer-pool-0 WAITING 1998 s3-committer-pool-0 WAITING 2022 s3-committer-pool-0 WAITING 2043 s3-committer-pool-0 WAITING 2416 s3-committer-pool-0 WAITING 2453 s3-committer-pool-0 WAITING 2470 s3-committer-pool-0 WAITING 2517 s3-committer-pool-0 WAITING 2534 s3-committer-pool-0 WAITING 2551 s3-committer-pool-0 WAITING 2580 s3-committer-pool-0 WAITING 2597 s3-committer-pool-0 WAITING 2614 s3-committer-pool-0 WAITING 2631 s3-committer-pool-0 WAITING 2726 s3-committer-pool-0 WAITING 2743 s3-committer-pool-0 WAITING 2763 s3-committer-pool-0 WAITING 2780 s3-committer-pool-0 WAITING 2819 s3-committer-pool-0 WAITING 2841 s3-committer-pool-0 WAITING 2858 s3-committer-pool-0 WAITING 2875 s3-committer-pool-0 WAITING 2925 s3-committer-pool-0 WAITING 2942 s3-committer-pool-0 WAITING 2963 s3-committer-pool-0 WAITING 2980 s3-committer-pool-0 WAITING 3020 s3-committer-pool-0 WAITING 3037 s3-committer-pool-0 WAITING 3055 s3-committer-pool-0 WAITING 3072 s3-committer-pool-0 WAITING 3127 s3-committer-pool-0 WAITING 3144 s3-committer-pool-0 WAITING 3163 s3-committer-pool-0 WAITING 3180 s3-committer-pool-0 WAITING 3222 s3-committer-pool-0 WAITING 3242 s3-committer-pool-0 WAITING 3259 s3-committer-pool-0 WAITING 3278 s3-committer-pool-0 WAITING 3418 s3-committer-pool-0 WAITING 3435 s3-committer-pool-0 WAITING 3452 s3-committer-pool-0 WAITING 3469 s3-committer-pool-0 WAITING 3486 s3-committer-pool-0 WAITING 3491 s3-committer-pool-0 WAITING 3501 s3-committer-pool-0 WAITING 3508 s3-committer-pool-0 WAITING 4029 s3-committer-pool-0 WAITING 4093 s3-committer-pool-0 WAITING 4658 s3-committer-pool-0 WAITING 4666 s3-committer-pool-0 WAITING 4907 s3-committer-pool-0 WAITING 5102 s3-committer-pool-0 WAITING 5119 s3-committer-pool-0 WAITING 5158 s3-committer-pool-0 WAITING 5175 s3-committer-pool-0 WAITING 5192 s3-committer-pool-0 WAITING 5209 s3-committer-pool-0 WAITING 5226 s3-committer-pool-0 WAITING 5395 s3-committer-pool-0 WAITING 5634 s3-committer-pool-0 WAITING 5651 s3-committer-pool-0 WAITING 5668 s3-committer-pool-0 WAITING 5685 s3-committer-pool-0 WAITING 5702 s3-committer-pool-0 WAITING 5722 s3-committer-pool-0 WAITING 5739 s3-committer-pool-0 WAITING 6144 s3-committer-pool-0 WAITING 6167 s3-committer-pool-0 WAITING 6289 s3-committer-pool-0 WAITING 6588 s3-committer-pool-0 WAITING 6628 s3-committer-pool-0 WAITING 6645 s3-committer-pool-0 WAITING 6662 s3-committer-pool-0 WAITING 6675 s3-committer-pool-0 WAITING 6692 s3-committer-pool-0 WAITING 6709 s3-committer-pool-0 WAITING 7049 s3-committer-pool-0 WAITING
- 当前s3a相关配置如下,配置的线程数上限远低于实际观测到的线程数:
fs.s3a.threads.max 100 fs.s3a.connection.maximum 1000 fs.s3a.committer.threads 16 fs.s3a.max.total.tasks 5 fs.s3a.committer.name directory fs.s3a.fast.upload.buffer disk io.file.buffer.size 1048576 mapreduce.outputcommitter.factory.scheme.s3a - org.apache.hadoop.fs.s3a.commit.S3ACommitterFactory
- 已测试多个版本的spark-hadoop-cloud依赖,问题均可稳定复现。
问题成因
- 核心原因为Hadoop已知bug HADOOP-17260:s3a committer绑定的线程池在S3AFileSystem实例关闭时没有被正确销毁,每初始化一个新的S3AFileSystem实例就会生成一个全新的s3-committer-pool线程池,闲置线程无法回收最终堆积到数千个,超出系统进程资源限制触发OOM。
- 你当前测试的所有spark-hadoop-cloud版本对应的底层Hadoop依赖均未合入该bug的修复逻辑,因此更换依赖包后问题仍然稳定复现。
- Spark Driver运行作业过程中会频繁创建新的S3AFileSystem实例访问S3存储,加速了线程泄漏的速度,短时间内就能达到进程线程数上限。
修复方案
根治方案
升级底层Hadoop依赖到3.3.1及以上版本,该版本已经合入HADOOP-17260的修复逻辑,线程池会随FileSystem实例关闭正常销毁,不会出现线程堆积问题。若使用CDH发行版,升级到7.2.15及以上版本即可解决问题。
临时规避方案
如果暂时无法升级Hadoop版本,可以通过添加以下配置强制S3AFileSystem实例全局复用,避免频繁创建新实例触发线程泄漏:
spark.hadoop.fs.s3a.impl.disable.cache false spark.hadoop.fs.s3a.cache.max.size 20
该配置会让Spark全局复用已创建的S3AFileSystem实例,大幅降低新实例创建频率,缓解线程堆积速度。
临时缓解方案
调高Driver进程的ulimit最大线程数限制(将ulimit -u参数调整到10000以上),可以避免作业短时间内崩溃,但无法彻底解决线程泄漏问题,适合作为过渡方案使用。
内容的提问来源于stack exchange,提问作者Ofer Eliassaf
相关产品推荐
相关产品推荐

