Apache spark Spark SQL:为什么一个查询有两个作业?

Apache spark Spark SQL:为什么一个查询有两个作业?,apache-spark,apache-spark-sql,unsafe,parquet,Apache Spark,Apache Spark Sql,Unsafe,Parquet,实验 我在Spark 1.6.1上尝试了以下代码片段 val soDF = sqlContext.read.parquet("/batchPoC/saleOrder") # This has 45 files soDF.registerTempTable("so") sqlContext.sql("select dpHour, count(*) as cnt from so group by dpHour order by cnt").write.parquet("/out/") 实体计划是:

实验

我在
Spark 1.6.1
上尝试了以下代码片段

val soDF = sqlContext.read.parquet("/batchPoC/saleOrder") # This has 45 files
soDF.registerTempTable("so")
sqlContext.sql("select dpHour, count(*) as cnt from so group by dpHour order by cnt").write.parquet("/out/")
实体计划是:

== Physical Plan ==
Sort [cnt#59L ASC], true, 0
+- ConvertToUnsafe
   +- Exchange rangepartitioning(cnt#59L ASC,200), None
      +- ConvertToSafe
         +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Final,isDistinct=false)], output=[dpHour#38,cnt#59L])
            +- TungstenExchange hashpartitioning(dpHour#38,200), None
               +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Partial,isDistinct=false)], output=[dpHour#38,count#63L])
                  +- Scan ParquetRelation[dpHour#38] InputPaths: hdfs://hdfsNode:8020/batchPoC/saleOrder
对于这个查询,我得到了两个作业:
job9
job10

对于
作业9
DAG
为:

== Physical Plan ==
Sort [cnt#59L ASC], true, 0
+- ConvertToUnsafe
   +- Exchange rangepartitioning(cnt#59L ASC,200), None
      +- ConvertToSafe
         +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Final,isDistinct=false)], output=[dpHour#38,cnt#59L])
            +- TungstenExchange hashpartitioning(dpHour#38,200), None
               +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Partial,isDistinct=false)], output=[dpHour#38,count#63L])
                  +- Scan ParquetRelation[dpHour#38] InputPaths: hdfs://hdfsNode:8020/batchPoC/saleOrder

对于
作业10
DAG
为:

== Physical Plan ==
Sort [cnt#59L ASC], true, 0
+- ConvertToUnsafe
   +- Exchange rangepartitioning(cnt#59L ASC,200), None
      +- ConvertToSafe
         +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Final,isDistinct=false)], output=[dpHour#38,cnt#59L])
            +- TungstenExchange hashpartitioning(dpHour#38,200), None
               +- TungstenAggregate(key=[dpHour#38], functions=[(count(1),mode=Partial,isDistinct=false)], output=[dpHour#38,count#63L])
                  +- Scan ParquetRelation[dpHour#38] InputPaths: hdfs://hdfsNode:8020/batchPoC/saleOrder

观察结果

  • 显然,一个查询有两个
    作业
    
  • Stage-16
    (在作业9
  • 中标记为
    Stage-14
    )在作业10
  • 中被跳过
  • Stage-15
    的最后一个
    RDD[48]
    ,与
    Stage-17
    的最后一个
    RDD[49]
    相同。怎么用?我在日志中看到,在执行
    Stage-15
    之后,
    RDD[48]
    被注册为
    RDD[49]
  • Stage-17
    显示在
    驱动程序日志中,但从未在
    执行器执行。在
    驱动程序日志上
    显示了任务执行情况,但当我查看
    纱线
    容器日志时,没有证据表明从
    阶段17
    收到任何
    任务
    支持这些观察结果的日志(只有
    驱动程序日志
    ,由于后来的崩溃,我丢失了
    执行器日志)。可以看出,在
    Stage-17
    开始之前,
    RDD[49]
    已注册:

    16/06/10 22:11:22 INFO TaskSetManager: Finished task 196.0 in stage 15.0 (TID 1121) in 21 ms on slave-1 (199/200)
    16/06/10 22:11:22 INFO TaskSetManager: Finished task 198.0 in stage 15.0 (TID 1123) in 20 ms on slave-1 (200/200)
    16/06/10 22:11:22 INFO YarnScheduler: Removed TaskSet 15.0, whose tasks have all completed, from pool 
    16/06/10 22:11:22 INFO DAGScheduler: ResultStage 15 (parquet at <console>:26) finished in 0.505 s
    16/06/10 22:11:22 INFO DAGScheduler: Job 9 finished: parquet at <console>:26, took 5.054011 s
    16/06/10 22:11:22 INFO ParquetRelation: Using default output committer for Parquet: org.apache.parquet.hadoop.ParquetOutputCommitter
    16/06/10 22:11:22 INFO FileOutputCommitter: File Output Committer Algorithm version is 1
    16/06/10 22:11:22 INFO DefaultWriterContainer: Using user defined output committer class org.apache.parquet.hadoop.ParquetOutputCommitter
    16/06/10 22:11:22 INFO FileOutputCommitter: File Output Committer Algorithm version is 1
    16/06/10 22:11:22 INFO SparkContext: Starting job: parquet at <console>:26
    16/06/10 22:11:22 INFO DAGScheduler: Registering RDD 49 (parquet at <console>:26)
    16/06/10 22:11:22 INFO DAGScheduler: Got job 10 (parquet at <console>:26) with 25 output partitions
    16/06/10 22:11:22 INFO DAGScheduler: Final stage: ResultStage 18 (parquet at <console>:26)
    16/06/10 22:11:22 INFO DAGScheduler: Parents of final stage: List(ShuffleMapStage 17)
    16/06/10 22:11:22 INFO DAGScheduler: Missing parents: List(ShuffleMapStage 17)
    16/06/10 22:11:22 INFO DAGScheduler: Submitting ShuffleMapStage 17 (MapPartitionsRDD[49] at parquet at <console>:26), which has no missing parents
    16/06/10 22:11:22 INFO MemoryStore: Block broadcast_25 stored as values in memory (estimated size 17.4 KB, free 512.3 KB)
    16/06/10 22:11:22 INFO MemoryStore: Block broadcast_25_piece0 stored as bytes in memory (estimated size 8.9 KB, free 521.2 KB)
    16/06/10 22:11:22 INFO BlockManagerInfo: Added broadcast_25_piece0 in memory on 172.16.20.57:44944 (size: 8.9 KB, free: 517.3 MB)
    16/06/10 22:11:22 INFO SparkContext: Created broadcast 25 from broadcast at DAGScheduler.scala:1006
    16/06/10 22:11:22 INFO DAGScheduler: Submitting 200 missing tasks from ShuffleMapStage 17 (MapPartitionsRDD[49] at parquet at <console>:26)
    16/06/10 22:11:22 INFO YarnScheduler: Adding task set 17.0 with 200 tasks
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 0.0 in stage 17.0 (TID 1125, slave-1, partition 0,NODE_LOCAL, 1988 bytes)
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 1.0 in stage 17.0 (TID 1126, slave-2, partition 1,NODE_LOCAL, 1988 bytes)
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 2.0 in stage 17.0 (TID 1127, slave-1, partition 2,NODE_LOCAL, 1988 bytes)
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 3.0 in stage 17.0 (TID 1128, slave-2, partition 3,NODE_LOCAL, 1988 bytes)
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 4.0 in stage 17.0 (TID 1129, slave-1, partition 4,NODE_LOCAL, 1988 bytes)
    16/06/10 22:11:23 INFO TaskSetManager: Starting task 5.0 in stage 17.0 (TID 1130, slave-2, partition 5,NODE_LOCAL, 1988 bytes)
    
    16/06/10 22:11:22信息任务集管理器:在slave-1(199/200)上以21毫秒的时间完成阶段15.0(TID 1121)中的任务196.0
    2010年6月16日22:11:22信息任务集管理器:在slave-1(200/200)上以20毫秒的时间完成阶段15.0(TID 1123)中的任务198.0
    2010年6月16日22:11:22信息源调度程序:已从池中删除任务集15.0,其任务已全部完成
    2010年6月16日22:11:22信息:结果第15阶段(镶木地板:26)以0.505秒完成
    2010年6月16日22:11:22信息调度程序:作业9已完成:镶木地板在:26,耗时5.054011秒
    2010年6月16日22:11:22信息拼花:使用拼花的默认输出提交程序:org.apache.Parquet.hadoop.ParquetOutputCommitter
    16/06/10 22:11:22信息文件输出提交人:文件输出提交人算法版本为1
    16/06/10 22:11:22信息DefaultWriterContainer:使用用户定义的输出提交程序类org.apache.parquet.hadoop.ParquetOutputCommitter
    16/06/10 22:11:22信息文件输出提交人:文件输出提交人算法版本为1
    16/06/10 22:11:22信息SparkContext:开始工作:拼花地板:26
    2010年6月16日22:11:22信息:注册RDD49(拼花地板位于:26)
    2010年6月16日22:11:22信息调度程序:获得了带有25个输出分区的作业10(拼花:26)
    2010年6月16日22:11:22信息:最后阶段:结果阶段18(拼花地板:26)
    2010年6月16日22:11:22信息:最后阶段的家长:列表(ShuffleMapStage 17)
    16/06/10 22:11:22信息调度程序:缺少的父母:列表(ShuffleMapStage 17)
    2010年6月16日22:11:22信息调度程序:提交ShuffleMapStage 17(MapPartitionsRDD[49]位于:26的拼花地板),其中没有丢失的父对象
    16/06/10 22:11:22信息内存存储:块广播存储为内存中的值(估计大小为17.4KB,可用大小为512.3KB)
    2010年6月16日22:11:22信息存储器存储:块广播\u 25\u片段0以字节形式存储在内存中(估计大小为8.9 KB,可用大小为521.2 KB)
    2010年6月16日22:11:22信息块管理信息:在172.16.20.57:44944的内存中添加了广播片段0(大小:8.9 KB,可用空间:517.3 MB)
    2010年6月16日22:11:22信息SparkContext:从DAGScheduler的广播创建了广播25。scala:1006
    2010年6月16日22:11:22信息调度程序:从ShuffleMapStage 17提交200个缺少的任务(拼花地板上的MapPartitionsRDD[49]在:26)
    2010年6月16日22:11:22信息YarnScheduler:添加包含200个任务的任务集17.0
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务0.0(TID 1125,从1,分区0,节点_本地,1988字节)
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务1.0(TID 1126,从属2,分区1,节点_本地,1988字节)
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务2.0(TID 1127,从机1,分区2,节点_本地,1988字节)
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务3.0(TID 1128,从属2,分区3,节点_本地,1988字节)
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务4.0(TID 1129,从机1,分区4,节点_本地,1988字节)
    2010年6月16日22:11:23信息任务集管理器:在阶段17.0中启动任务5.0(TID 1130,从属2,分区5,节点_本地,1988字节)
    
    问题

  • 为什么有两个
    作业
    ?将一个
    DAG
    分为两个
    作业
    ,其目的是什么
  • Job 10
    DAG
    看起来完成了查询执行。作业9有什么具体的工作吗
  • 为什么不跳过第17阶段?似乎创建了虚拟
    任务
    ,它们是否有任何用途
  • 后来,我尝试了另一个更简单的查询。出乎意料的是,它创造了3个
    工作岗位

    sqlContext.sql(“按dpHour排序从so中选择dpHour”).write.parquet(“/out2/”)


  • 使用高级数据帧/数据集API时,由Spark决定执行计划,包括作业/阶段分块。这些取决于许多因素,如执行并行性、缓存/持久化数据结构等。在Spark的未来版本中,随着优化器复杂性的提高,您可能会看到每个查询有更多的作业,例如,对一些数据源进行采样以参数化基于成本的执行优化

    例如,我经常(但并非总是)看到写作与涉及洗牌的处理产生不同的作业

    总之,如果您使用的是高级API,除非您必须对巨大的数据量进行极其详细的优化,否则深入研究特定的分块很少有好处。与处理/输出相比,作业启动成本极低

    另一方面,如果你对这个世界的火花感到好奇