Dataframes的动态转换管道

时间:2017-04-23 15:10:32

标签: apache-spark apache-spark-sql

是否可以动态构建转换管道并在数据集上执行?想象一下,我有一个数据集ds。我想做像ds.filter.join.join.filter这样的事情。现在,管道本身和转换的参数(如过滤器等)将是动态的(取自用户查询,想象一个描述管道的xml或json)。

我觉得编写一些使用普通java反射的东西会很简单。但是,我想知道是否有更好的方法来执行此操作 - 例如动态构建管道并执行ds.execute(dynamicPipeline)。

新手激发并在互联网/论坛上合理搜索此事。任何指针都赞赏。使用Spark 2+和Java 8。

1 个答案:

答案 0 :(得分:0)

tl; dr 请不要使用反射。请改用库或Scala语言的功能。

为了对世界上所有人的爱,请不要使用反思。写入很麻烦,这使得反射代码容易出错,对于所有低级别的工作,你甚至不会获得性能提升。事实上,情况正好相反。

与此同时,想要更高级别的管道编排工作是相当普遍的。例如,您可能有一个有效的遗留MapReduce作业,并且您希望将输出序列文件提供给Spark;您的Spark作业的输出转到Hive,在那里您可以使用传统的Hive查询。等等。有像Oozie这样的开源管道工具可以提供帮助。级联是另一个,但我不相信它有Spark支持。

然而,如果你喜欢采用Suffering-Oriented Programming方法,而你最初喜欢较轻的解决方案,只有在没有它们的痛苦变得太大而无法承受的情况下才会带上大枪,那么也许你可以利用Scala语言的功能来解决问题。

我不确定你希望做什么,但是例如,你可能有

trait Foo {
  def sparkSession: SparkSession
  def pipeline: Dataset[Bar] => Dataset[Baz]

  val ds = sparkSession.read(...).as[Bar]
  val dsAfterPipeline = pipeline(ds)
}

class Driver extends App with Foo {
   val sparkSession: SparkSession = ...
   val pipeline: DataSet[Bar] => DataSet[Baz] = ??? //generate pipeline as needed

   dsAfterPipeline.write.parquet("file.parquet")
}

正如您在此处所看到的,所有工作都发生在Foo特征中,但该特征要求将其连接到提供SparkSession 的类中表示管道的函数。只要符合Driver中指定的合同,就可以在Foo中看到它。{/ p>

这种方法将管道代码与业务流程分离,并且它还使测试更容易,因为您可以提供满足Dataset[Bar] => Dataset[Baz]的任何函数。

最后,您可以将此方法与Scala中的implicit类(重新排列一些内容)结合起来,以丰富(或#34; pimp")DataSet类来添加{ {1}}接受管道的功能,但是你提供它:

execute

此时你可以这样做:

object Helper {
  implicit class DataSetOps[Bar, Baz](ds: Dataset[Bar]) {
    def execute(pipeline: Dataset[Bar] => Dataset[Baz]) = ???
  }
}

其中import Helper._ sparkSession.read(...).as[Bar].execute(pipeline) 的类型为pipeline

同样,我不知道你真正想做什么,但希望你可以从Scala语言提供的内容或管道库中收集一些想法以获得你需要的东西。

最重要的是避免反思。