这更像是一个想法,而不是一个完整的解决方案,我还没有测试过。
您可以先将数据处理管道提取到函数中。
def pipeline(f: String, n: Int) = {
sqlContext
.read
.format("com.databricks.spark.csv")
.option("header", "true")
.load(f)
.repartition(n)
.groupBy(...)
.agg(...)
.cache // Cache so we can force computation later
}
如果您的文件很小,您可以调整n 参数以使用尽可能少的分区来容纳单个文件中的数据并避免混洗。这意味着您正在限制并发,但我们稍后会回到这个问题。
val n: Int = ???
接下来,您必须获取输入文件的列表。此步骤取决于数据源,但大多数情况下它或多或少是简单的:
val files: Array[String] = ???
接下来你可以使用pipeline函数映射上面的列表:
val rdds = files.map(f => pipeline(f, n))
由于我们将并发限制在单个文件级别,因此我们希望通过提交多个作业来进行补偿。让我们添加一个简单的帮助器来强制评估并用 Future 包装它
import scala.concurrent._
import ExecutionContext.Implicits.global
def pipelineToFuture(df: org.apache.spark.sql.DataFrame) = future {
df.rdd.foreach(_ => ()) // Force computation
df
}
最后我们可以在rdds 上使用上面的助手:
val result = Future.sequence(
rdds.map(rdd => pipelineToFuture(rdd)).toList
)
根据您的要求,您可以添加 onComplete 回调或使用响应式流来收集结果。