Apache Spark无需缓存即可写入多个输出(不同的镶木模式)

时间:2019-03-01 09:51:30

标签: apache-spark apache-spark-sql

我想转换我的输入数据(XML文件)并产生3个不同的输出。

每个输出将采用镶木地板格式,并具有不同的架构/列数。

当前在我的解决方案中,数据存储在RDD[Row]中,其中每个行属于三种类型之一,并且具有不同数量的字段。我现在正在做的是缓存RDD,然后对其进行过滤(使用告诉记录类型的字段)并使用以下方法保存数据:

var resultDF_1 = sqlContext.createDataFrame(filtered_data_1, schema_1)
resultDF_1.write.parquet(output_path_1)
...
// the same for filtered_data_2 and filtered_data_3

有什么方法可以做得更好,例如不要在内存中缓存整个数据?

在MapReduce中,我们具有MultipleOutputs类,我们可以通过以下方式做到这一点:

MultipleOutputs.addNamedOutput(job, "data_type_1", DataType1OutputFormat.class, Void.class, Group.class);
MultipleOutputs.addNamedOutput(job, "data_type_2", DataType2OutputFormat.class, Void.class, Group.class);
MultipleOutputs.addNamedOutput(job, "data_type_3", DataType3OutputFormat.class, Void.class, Group.class);
...
MultipleOutputs<Void, Group> mos = new MultipleOutputs<>(context);
mos.write("data_type_1", null, myRecordGroup1, filePath1);
mos.write("data_type_2", null, myRecordGroup2, filePath2);
...

1 个答案:

答案 0 :(得分:0)

AFAIK,无法将一个RDD本身拆分为多个RDD。这就是Spark的DAG的工作方式:只有子RDD从父RDD中提取数据。

但是,我们可以从同一个父RDD中读取多个子RDD。为了避免重新计算父RDD,除了缓存之外别无其他方法。 我假设您由于担心内存不足而希望避免缓存。我们可以通过将RDD保留为MEMORY_AND_DISK来避免出现内存不足(OOM)问题,这样大的RDD就会溢出必要时将其插入磁盘。

让我们从您的原始数据开始:

val allDataRDD = sc.parallelize(Seq(Row(1,1,1),Row(2,2,2),Row(3,3,3)))

我们可以先将其保留在内存中,但在内存不足的情况下允许其溢出到磁盘上:

allDataRDD.persist(StorageLevel.MEMORY_AND_DISK)

然后我们创建3个RDD输出:

filtered_data_1 = allDataRDD.filter(_.get(1)==1) // //
filtered_data_2 = allDataRDD.filter(_.get(2)==1) // use your own filter funcs here
filtered_data_3 = allDataRDD.filter(_.get(3)==1) // //

然后我们写输出:

var resultDF_1 = sqlContext.createDataFrame(filtered_data_1, schema_1)
resultDF_1.write.parquet(output_path_1)
var resultDF_2 = sqlContext.createDataFrame(filtered_data_2, schema_2)
resultDF_2.write.parquet(output_path_2)
var resultDF_3 = sqlContext.createDataFrame(filtered_data_3, schema_3)
resultDF_3.write.parquet(output_path_3)

如果您确实要避免多次通过,可以使用自定义分区程序来解决。您可以将数据重新划分为3个分区,每个分区都有自己的任务,因此也有自己的输出文件/部分。需要注意的是,并行性将被大量减少到3个线程/任务,并且还存在大于2GB的数据存储在单个分区中的风险(Spark的每个分区限制为2GB)。我没有为此方法提供详细的代码,因为我认为它无法编写具有不同架构的镶木地板文件。