Spark中的Job、Stage、Task

原文链接:https://www.jianshu.com/p/e1243537d0fd

写在前面

台风夜的电话面试里被问到了spark运行任务的过程中stage的划分依据。一下子就给整懵了,支支吾吾答非所问。从事大数据的开发也有一年半光景,spark任务的运行原理依旧知之甚少。因此就参阅各种优秀的文章,再配上一个自己工作中的实际项目,特意整理出这篇笔记,以此警示自己的自大与无知。

测试环境

本地开发环境

idea 2019.1.2

maven 3.6

spark 2.4.3

scala 2.1.8

jdk1.8

测试集群环境

spark 2.4.3

scala 2.1.8

jdk1.8

hadoop2.7.4

集群

测试项目例子

计算某一天店铺销售额\时段销售额top10

样例数据字段格式

filed1|filed2|filed3|store_no|filed5|filed6|filed7|filed8|amount|filed10|filed11|sale_time

这里不提供具体的测试数据,实验过程中需要自己模拟所用的数据。

样例demo

packagecom.dr.leoimportcom.dr.leo.utils.StrUtilsimportorg.apache.hadoop.io.{LongWritable,Text}importorg.apache.hadoop.mapred.{FileSplit,InputSplit,TextInputFormat}importorg.apache.spark.SparkContextimportorg.apache.spark.rdd.{HadoopRDD,RDD}importorg.apache.spark.sql.SparkSession/**

  * @author leo.jie (weixiao.me@aliyun.com)

  * @organization DataReal

  * @version 1.0

  * @website https://www.jlpyyf.com

  * @date 2019-07-28 20:29

  * @since 1.0

  */objectWordCount{defmain(args:Array[String]):Unit={valspark:SparkSession=SparkSession.builder().appName("WordCount")//.master("local[*]").enableHiveSupport().getOrCreate()valsc:SparkContext=spark.sparkContextvalfileRddOri=loadFileToRdd(sc,"hdfs://leo/test/pos.DAT")valfileRdd=fileRddOri.map(x=>for(data<-x._2.split("\\|"))yieldif(data==null)""elsedata.trim).filter(x=>x.length==12).map(x=>(retailer_shop_code(x(3)),x(10).substring(10,13),x(8).toFloat)).map(x=>((x._1,x._2),x._3)).reduceByKey(_+_)fileRdd.top(10)(Ordering.by(e=>e._2)).foreach(println(_))println("##########################################################")fileRdd.map(x=>(x._1._1,x._2)).reduceByKey(_+_).top(10)(Ordering.by(e=>e._2)).foreach(println(_))println("##########################################################")println(fileRdd.count())println("##########################################################")println(fileRdd.first())println("##########################################################")fileRdd.take(10).foreach(println(_))while(true){;}spark.stop()}/**

    * 读取gbk编码的file

    * @param sc

    * @param path

    * @param encoding

    * @return

    */defloadFileToRdd(sc:SparkContext,path:String,encoding:String="GBK"):RDD[(String,String,Int)]={sc.hadoopFile[LongWritable,Text,TextInputFormat](path).asInstanceOf[HadoopRDD[LongWritable,Text]].mapPartitionsWithInputSplit((inputSplit:InputSplit,iterator:Iterator[(LongWritable,Text)])=>{valfile=inputSplit.asInstanceOf[FileSplit]iterator.filter(x=>x._2!=null).map(x=>{(file.getPath.getName,newString(x._2.getBytes,0,x._2.getLength,encoding),1)})})}/**

    * 只是一个店铺号转换的函数

    * @param retailer_shop_code

    * @return

    */defretailer_shop_code(retailer_shop_code:String):String={if(StrUtils.isBlank(retailer_shop_code))""elseif(retailer_shop_code.length==5)retailer_shop_code.substring(0,retailer_shop_code.length-1).toUpperCase()elseif(retailer_shop_code.length==6)retailer_shop_code.substring(0,retailer_shop_code.length-2).toUpperCase()elseif(retailer_shop_code.length==8)retailer_shop_code.substring(0,retailer_shop_code.length-2).toUpperCase()elseretailer_shop_code}}

运行测试

程序打包后发往集群提交任务。所用命令

[hadoop@node1leo_demo]$ spark-submit--master yarn--deploy-mode cluster--driver-memory2G--driver-cores2--executor-memory2g--num-executors5--executor-cores2--conf spark.yarn.executor.memoryOverhead=2048--conf spark.network.timeout=30000--classcom.dr.leo.WordCountleo-study-spark.jar

spark-ui上的信息告诉了我们什么?

查看任务的运行信息

spark任务运行的过程中,我们可以点击 <code style="color:red">ApplicationMaster</code> 跳转任务运行的界面。

yarn

运行流程之:job

job

此时我们提交的任务的所有job都已经运行成功,只因为程序中任务执行完毕后是一段无限循环,所以这个界面会一直存在,直到我们手动在yarn上kill掉这个application。

我们写的代码被提交运行的过程中,会先被划分为一个又一个job,这些job按照被划分的先后顺序会依次执行。

图示中我们已经知道,我们提交的任务,最终被划分成了5个job。

所谓一个job,就是由一个rdd action触发的动作。简单理解为,当你需要执行一个rdd的action操作的时候,就会生成一个job。

这里不会赘述什么是rdd的action操作。

结合代码与图示我们可以知道:

job-0 的产生是由于触发了top操作

<code style="color:red">top at WordCount.scala:33</code>

job-1 的产生是由于触发了top操作

<code style="color:red">top at WordCount.scala:35</code>

job-2 的产生是由于触发了count操作

<code style="color:red">count at WordCount.scala:37</code>

job-3 的产生是由于触发了first操作

<code style="color:red">first at WordCount.scala:39</code>

job-4 的产生是由于触发了take操作

<code style="color:red">take at WordCount.scala:41</code>

运行流程之:stage

选择任意一个job,点击链接去查看该job的detail,这里我们选择job-0。

job-detail

由图示我们可知,job-0由两个stage组成,并且每个stage都有8个task,说明每个stage的数据都在8个partition上。

下面我们将详细说明stage以及stage的划分依据。

stage 概念、划分

貌似没有十分明确的概念来十分清楚地说明spark stage究竟是什么?这里只记录从众多优秀的博客里提取的直言片语,以及本人的一点见解。

stage的划分是以shuffle操作作为边界的。也就是说某个action导致了shuffle操作,就会划分出两个stage。

stage的划分在RDD的论文中也有详细介绍,简单的说是以shuffle和result这两种类型来划分。在spark中有两类task,一类是shuffle map task,一类是result task,第一类task的输出是shuffle所需数据,第二类task的输出是result,stage的划分也依次为依据,shuffle之前的所有变换是一个stage,shuffle之后的操作是另一个stage。比如 rdd.parallize(1 to 10).foreach(println) 这个操作没有shuffle,直接就输出了,那么只有它的task是resultTask,stage也只有一个;如果是rdd.map(x => (x, 1)).reduceByKey(_ + _).foreach(println), 这个job因为有reduce,所以有一个shuffle过程,那么reduceByKey之前的是一个stage,执行shuffleMapTask,输出shuffle所需的数据,reduceByKey到最后是一个stage,直接就输出结果了。如果job中有多次shuffle,那么每个shuffle之前都是一个stage。

在DAGScheduler中,会将每个job划分成多个stage,每个stage会创建一批task并且计算task的最佳位置,一个task对应一个partition。DAGScheduler的stage划分算法如下:它会从触发action操作的那个RDD开始往前推,首先会为最后一个RDD创建一个stage,然后往前倒推的时候,如果发现对某个RDD是宽依赖,那么就会将宽依赖的那个RDD创建一个新的stage,那个RDD就是新的stage的最后一个RDD,然后依次类推,继续往前倒推,根据窄依赖或者宽依赖进行stage的划分,直到所有的RDD全部遍历完成为止。

spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG会提交给DAGScheduler,DAGScheduler会把DAG划分成相互依赖的多个stage,划分stage的依据就是RDD之间的宽窄依赖。<code style="color:red">遇到宽依赖就划分stage</code>,每个stage包含一个或多个task任务。然后将这些task以task set的形式交给TaskScheduler运行。<code style="color:red">stage是由一组并行的task组成</code>。

窄依赖

父RDD和子RDD partition之间的关系是一对一的。不会有shuffle的产生。父RDD的一个分区去到子RDD的一个分区中。

宽依赖

父RDD与子RDD partition之间的关系是一对多的。会有shuffle的产生。父RDD的一个分区去到子RDD的不同分区里面。

其实区分宽窄依赖,主要就是看父RDD的一个partition的流向,要是流向一个的话就是窄依赖,流向多个的话就是宽依赖。以WordCount为例,看图理解:

spark stage

运行流程之:task

task是stage下的一个任务执行单元,一般来说,一个rdd有多少个partition,就会有多少个task,因为每一个task只是处理一个partition上的数据。

作者:NikolasNull

链接:https://www.jianshu.com/p/e1243537d0fd

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270