我构建了一个Spark RDD,其中此RDD的每个元素都是一个表示XML记录的JAXB根元素。
我想拆分这个RDD,以便从这个集合产生6个RDD。本质上,这项工作只是将分层XML结构转换为6组平坦的CSV记录。我目前正在六次通过相同的RDD 6来做这件事。
xmlRdd.cache()
val rddofTypeA = xmlRdd.map {iterate over XML Object and create Type A}
rddOfTypeA.saveAsTextFile("s3://...")
val rddofTypeB = xmlRdd.map { iterate over XML Object and create Type B}
rddOfTypeB.saveAsTextFile("s3://...")
val rddofTypeC = xmlRdd.map { iterate over XML Object and create Type C}
rddOfTypeC.saveAsTextFile("s3://...")
val rddofTypeD = xmlRdd.map { iterate over XML Object and create Type D}
rddOfTypeD.saveAsTextFile("s3://...")
val rddofTypeE = xmlRdd.map { iterate over XML Object and create Type E}
rddOfTypeE.saveAsTextFile("s3://...")
val rddofTypeF = xmlRdd.map { iterate over XML Object and create Type F}
rddOfTypeF.saveAsTextFile("s3://...")
我的输入数据集是35百万条记录,分为186个文件,每个448MB,存储在Amazon S3中。我的输出目录也在S3上。我正在使用EMR Spark。
使用六节点m4.4xlarge群集,需要38分钟才能完成此分割并写入输出。
有没有一种有效的方法来实现这一目标,而不是在RDD上行走六次?
答案 0 :(得分:5)
最简单的解决方案(从Spark开发人员的角度来看)是在单独的线程上为每个RDD执行map
和saveAsTextFile
。
未广为人知(并因此被利用)的事实是SparkContext
是线程安全的,因此可用于从单独的线程提交作业。
话虽如此,您可以执行以下操作(使用最简单的Scala解决方案Future
,但不一定是最好的,因为Future
在实例化时启动计算,而不是在您说的时候):
xmlRdd.cache()
import scala.concurrent.ExecutionContext.Implicits.global
val f1 = Future {
val rddofTypeA = xmlRdd.map { map xml to csv}
rddOfTypeA.saveAsTextFile("s3://...")
}
val f2 = Future {
val rddofTypeB = xmlRdd.map { map xml to csv}
rddOfTypeB.saveAsTextFile("s3://...")
}
...
Future.sequence(Seq(f1,f2)).onComplete { ... }
这可能会缩短进行映射和保存的时间,但不会减少数据集上的扫描次数。这应该不是什么大问题,因为数据集是缓存的,因此在内存和/或磁盘中(Spark SQL的MEMORY_AND_DISK
中的默认持久性级别为Dataset.cache
。