为Spark迭代take()或批处理?

时间:2015-10-22 01:39:48

标签: apache-spark

我有一个我从Spark访问的数据集(例如Parquet文件),它包含大量行。我需要将这些行中的一些数据发送到外部服务,我需要对它们进行批处理,以便每次对外部服务的调用都包含一定数量的行(例如,每批1000行)。基本上take(n)正在做什么,但是在大型数据集上重复,迭代地进行。执行此类任务的好方法是什么?我想可以用foreach()完成并手动批量聚合数据,但我想知道是否有任何内置/推荐的方法。

2 个答案:

答案 0 :(得分:5)

我不知道任何内置或推荐的选项,但简单的解决方案是结合RDD API和Scala Iterable API。如果你申请的操作是幂等的,你可以直接从工人那里做到:

val batchSize: Int = ???
val rdd: RDD[T] = ???
def doSomething(xs: Seq[T]) = ???

rdd.foreachPartition(_.grouped(batchSize).foreach(doSomething))

否则,您可以在此时向驱动程序提取单个分区:

rdd.cache
rdd.toLocalIterator.grouped(batchSize).foreach(doSomething)

请注意,每个分区需要单独的作业,因此最好先缓存输入rdd以避免重新计算。

在Python中,您可以使用toolz库替代Iterator API:

from toolz.itertoolz import partition_all

rdd.foreachPartition(
  lambda iter: [doSomething(xs) for xs in partition_all(batchSize, iter)])

for xs in partition_all(batchSize, rdd.toLocalIterator()):
    doSomething(xs)

答案 1 :(得分:4)

当您从镶木地板文件创建DataFrame时,它会根据HDFS块位置进行分区。

首先要问的是,您是否可以将数据集并行写入外部服务。即同时从多个服务器发送1000行的批次。

如果可以,那么最有效的方法是foreachPartition功能。类似的东西:

df.rdd.forEachPartition { it =>
  it.grouped(1000).foreach(sendBatch)
}

如果您的外部服务无法以这种方式使用,那么第二个最佳选择是toLocalIterator

df.rdd.toLocalIterator { it =>
  it.grouped(1000).foreach(sendBatch)
}

请注意,此解决方案的效率明显较低,因为它会序列化每个分区并将其从执行程序传输到驱动程序。

相关问题