Spark-Submit提交任务执行流程

spark-submit提交任务时,如果部署模式是cluster时,那么设置driver-memory的意义是啥?怎么理解?

对于spark-submit在cluster模式下设置driver-memory的理解如下:

在cluster模式下,driver程序运行在集群中的一个worker节点上,不再运行在提交 Spark 应用的客户端节点上。
driver程序同样需要消耗一定的内存资源,设置driver-memory就是为其在集群节点上申请相应的内存大小。
如果不设置,driver将使用该节点的默认内存配置,可能会与其他进程发生内存竞争,影响任务运行。
设置driver-memory后,spark会为driver程序在该节点上专门申请指定大小的内存,避免内存竞争问题。
driver-memory的大小需要根据应用程序的复杂度和需求进行评估设定,一般设置为1-2G比较合适。设置太大会浪费资源,太小又可能会导致内存不足。
需要注意的是,这个driver内存只在该节点上生效,不会从整个集群总内存中划分出来。
总体来说,设置driver-memory主要是为了给driver程序在集群节点上申请独占的内存空间,从而获得更稳定可靠的运行环境。

那如果是client模式时,设置的driver-memory其实就是在spark-submit提交的机器上申请相应的内存资源?

是的,您的理解正确。

在Spark的client模式下,driver程序运行在提交Spark应用的客户端节点上。

此时设置的driver-memory指定的内存资源也是在该客户端节点上申请,不再是在集群的worker节点上。

相比于cluster模式,client模式下driver-memory的作用是:

为driver程序申请独占的内存空间,避免与客户端上其他进程发生内存竞争。
按需设置driver程序需要的内存大小,避免内存溢出错误。
容纳driver程序运行过程中需要加载的数据结构,比如执行计划、Broadcast变量等。
该内存只在客户端上生效,不会从集群总内存中划分。
如果不设置,driver会占用客户端默认可用内存,可能导致客户端内存不足。
所以无论是cluster模式还是client模式下,设置driver-memory的目的都是给driver程序专门划分独占的内存空间,让其运行更稳定可靠。但作用的具体区别在于cluster模式下是在worker节点上申请,而client模式下是在提交客户端上申请。

那么上文中你一直说的driver程序又是指什么呢?可否具体解释下。以java语言下的spark sql api方式解释,例如读取hdfs上的parquet文件,处理,再输出到hdfs上

您提出了一个非常好的问题,什么是driver程序需要结合具体的Spark应用来解释更清楚。

以Java语言的Spark SQL为例,读取HDFS上的Parquet文件,处理后输出到HDFS上的流程如下:

在提交Spark应用的客户端节点上,会启动一个SparkContext,这个SparkContext就是driver程序。
driver程序包含了Spark应用的main()方法,负责构建SparkContext,定义transformations和actions,并向集群提交执行。
例如,driver程序会创建Dataset/DataFrame,加载Parquet文件,定义各种转换和行动操作后触发执行。
在客户端,driver程序会跟踪执行流程,请求Executor向其汇报状态。
Executor进程在worker节点上负责实际执行计算和存储任务。
最后driver程序负责输出执行结果,或者写入外部存储如HDFS。
所以在这个过程中,负责整个应用调度协调的SparkContext实例,以及包含主逻辑的main()方法,都可以认为是driver程序。它是发出执行调用的主要进程。

而Executor只负责具体的计算和存储任务。

设置driver-memory就是给SparkContext这个driver程序在客户端或集群上申请专属内存空间。

可否给个代码示例,来详细说下哪些部分的代码是driver程序,哪一部分又是会发生shuffle,stage,task等,以及哪些交由executor执行,以及stage的生成原理

您提出的非常好,代码示例可以更直观地说明Spark应用的执行流程。我尝试用Java写一个简单的WordCount应用来分析:

public class WordCount {

  public static void main(String[] args) {

    // 创建SparkContext,该对象为driver程序
    SparkContext sc = new SparkContext(sparkConf);
    
    // 读取文本文件,生成初始RDD,此时并无实际计算
    JavaRDD<String> lines = sc.textFile("hdfs:///file.txt");
    
    // 定义map转换操作,将文本行拆分成单词,这是transformation
    JavaRDD<String> words = lines.flatMap(s -> Arrays.asList(s.split(" ")));
    
    // 定义reduceByKey操作对单词计数,这是transformation
    JavaRDD<String> counts = words.mapToPair(w -> new Tuple2<>(w, 1))
                                  .reduceByKey((v1, v2) -> v1 + v2);
    
    // 执行行动count算子,触发实际计算,此时提交stage
    counts.count(); 
  }

}

在上述代码中:

main()方法及SparkContext对象为driver程序。
textFile()生成RDD,此时不会触发实际计算。
flatMap()和mapToPair()是transformation,定义计算逻辑。
count()为action,触发stage的生成和task执行。
stage的边界根据RDD的血缘关系生成,上例中textFile到counts之间为一个stage。
task根据数据分区来并行执行,比如对counts RDD做count时会启动多个task。
task在Executor上运行,包含实际的数据加载、转换计算等工作。
Executor不断向driver汇报task状态。
driver收集task结果,整个作业完成。
所以driver负责发起执行调用,既包含主程序也可看作控制节点。转换操作构建DAG图,action切分stage,由Executor上的task实例并行运行。希望这个示例可以恰当地解释Spark应用的基本执行流程。

posted @ 2023-08-16 22:49  LoremMoon  阅读(83)  评论(0编辑  收藏  举报