Spark:如何并行化每个数据帧分区上的后续特定工作

时间:2018-10-01 18:13:59

标签: scala apache-spark

我的Spark应用程序如下:

  

1)使用Spark SQL在数据框“ dataDF”中执行大型查询

     

2)foreach分区涉及“ dataDF”:

     

2.1)获取关联的“已过滤”数据帧,以便仅使分区关联数据

     

2.2)对“过滤后的”数据帧进行特定工作并写入输出

代码如下:

val dataSQL = spark.sql("SELECT ...")
val dataDF = dataSQL.repartition($"partition")

for {
  row <- dataDF.dropDuplicates("partition").collect
} yield {

   val partition_str : String = row.getAs[String](0)
   val filtered = dataDF.filter($"partition" .equalTo( lit( partition_str ) ) )

   // ... on each partition, do work depending on the partition, and write result on HDFS

   // Example :

   if( partition_str == "category_A" ){

       // do group by, do pivot, do mean, ...
       val x = filtered
         .groupBy("column1","column2")
         ...

       // write final DF
       x.write.parquet("some/path")

   } else if( partition_str == "category_B" ) {

       // select specific field and apply calculation on it
       val y = filtered.select(...)

       // write final DF
       x.write.parquet("some/path")

   } else if ( ... ) {

      // other kind of calculation
      // write results

   } else {

      // other kind of calculation
      // write results

   }

}

此类算法成功运行。 Spark SQL查询已完全分发。但是,对每个结果分区进行的特定工作是按顺序进行的,结果是无效的,尤其是因为与分区相关的每次写操作都是按顺序进行的。

在这种情况下,用并行/异步方式替换“ for yield”的方法是什么?

谢谢

1 个答案:

答案 0 :(得分:0)

  1. 如果使用特定环境所需的特定逻辑写入Hadoop范围之外的数据存储,则可以使用foreachPartition。

  2. 其他地图等

  3. .par并行集合(Scala)-谨慎使用。用于读取文件并对其进行预处理,否则可能会带来风险。

  4. 线程。

  5. 您需要检查正在执行的操作以及是否可以引用该操作,在foreachPartition块内使用wd等。您需要尝试一下,因为某些方面只能为驱动程序编写,然后才能分发给驱动程序。执行者通过SPARK给工人。但是,您不能按照以下方式为worker编写spark.sql-最后,由于某些格式化方面的错误,我刚在此处输入了文本块。请参阅发布结束。

  6. 在下面同样不能使用df.write或df.read。您可以做的是将单独的execute / mutate语句写入ORACLE,mySQL。

希望这会有所帮助。

rdd.foreachPartition(iter => {
       while(iter.hasNext) {
         val item = iter.next()
         // do something
         spark.sql("INSERT INTO tableX VALUES(2,7, 'CORN', 100, item)")
         // do some other stuff
  })

RDD.foreachPartition (records => {       
  val JDBCDriver = "com.mysql.jdbc.Driver" ...
  ...
  connectionProperties.put("user", s"${jdbcUsername}")
  connectionProperties.put("password", s"${jdbcPassword}")
 val connection = DriverManager.getConnection(ConnectionURL, jdbcUsername, jdbcPassword)
  ...
  val mutateStatement = connection.createStatement()
  val queryStatement = connection.createStatement()
  ...
      records.foreach (record => { 
              val val1 = record._1
              val val2 = record._2
              ...
              mutateStatement.execute (s"insert into sample (k,v) values(${val1}, ${nIterVal})")      
            })
  }            
)