我正在尝试处理存储桶中的json文件并将结果写入存储桶:
DataflowPipelineOptions options = PipelineOptionsFactory.create()
.as(DataflowPipelineOptions.class);
options.setRunner(BlockingDataflowPipelineRunner.class);
options.setProject("the-project");
options.setStagingLocation("gs://some-bucket/temp/");
Pipeline p = Pipeline.create(options);
p.apply(TextIO.Read.from("gs://some-bucket/2016/04/28/*/*.json"))
.apply(ParDo.named("SanitizeJson").of(new DoFn<String, String>() {
@Override
public void processElement(ProcessContext c) {
try {
JsonFactory factory = JacksonFactory.getDefaultInstance();
String json = c.element();
SomeClass e = factory.fromString(json, SomeClass.class);
// manipulate the object a bit...
c.output(factory.toString(e));
} catch (Exception err) {
LOG.error("Failed to process element: " + c.element(), err);
}
}
}))
.apply(TextIO.Write.to("gs://some-bucket/output/"));
p.run();
我在路径gs:// some-bucket / 2016/04/28 /(在子目录中)下有大约50,000个文件。 我的问题是:这需要一个多小时才能完成吗?在亚马逊的Spark群集上做类似的事情需要大约15-20分钟。我怀疑我可能效率低下。
编辑:
在我的Spark作业中,我将所有结果聚合在一个DataFrame中,然后一次性写入输出。我注意到我的管道在这里分别写了每个文件,我想这就是为什么它需要更长的时间。有没有办法改变这种行为?
答案 0 :(得分:0)
您的作业在Dataflow中遇到了几个性能问题,这是因为它更适合以更大的增量执行工作,而您的工作是处理大量非常小的文件。结果,作业执行的某些方面最终由每个文件开销占主导地位。这是一些细节和建议。
withNumShards
上指定TextIO.Write
来显着降低开销,具体取决于您在输出中需要多少文件。例如。 100
可能是合理的价值。默认情况下,你得到一个未指定数量的文件,在这种情况下,给定Dataflow优化器的当前行为,匹配输入文件的数量:通常这是一个好主意,因为它允许我们不实现中间数据,但在此这不是一个好主意,因为输入文件太小,每个文件的开销更重要。maxNumWorkers
设置为例如maxNumWorkers
之类的值12 - 目前第二项工作是自动扩展到过多的工人。这是由于Dataflow的自动调节当前面向以更大增量处理数据的作业 - 它目前没有考虑到每个文件的开销,并且在您的情况下表现不佳。maxNumWorkers=12
也应该使其成功完成。不久之后:
TextIO.Write.to("...").withNumShards(100)
bin
它应该运行得更好。