胶水作业将多个分区写入同一文件

时间:2020-07-23 18:46:38

标签: pyspark aws-glue

我正在尝试编写一个胶粘作业,该操作使用文件的csv的每一行将多个csv文件转换为单独的json文件。作业完成后,s3中会显示正确数量的文件,但是有些文件为空,有些文件中包含多个json对象。

应用映射后,这就是创建分区和写入文件的方式:

numEntities = applyMapping1.toDF().count()
partitions = applymapping1.repartition(numEntities)
partitions.toDF().write.mode("ignore").format("json").option("header", "true").save("s3://location/test")

使用此文件,一些文件被创建为一个json文件,该文件具有一个接一个的2个对象,有些是正确的,有些是空的。

有什么方法可以确保每个分区创建一个仅包含其数据的单独文件?

2 个答案:

答案 0 :(得分:0)

我认为repartition后面的Partitioner确实不完全符合您的意愿:

它创建了所需数量的分区-到目前为止,一切都很好。但是它并没有将行仅分配到每个分区中。这可能是由于HashPartitioner中的逻辑已经为多个行计算了相同的哈希值。

作为repartition.save...的替代方法,您可以使用foreachPartition,然后遍历每一行,将其保存到文件中(例如,在/tmp下),然后将其上传到S3。在此之前,我不会repartition,因为将要从foreachPartition执行的UDF相当昂贵,因此您应尽量减少UDF调用的次数。

这是一个对我有用的例子。它是用Scala编写的:

dynamicFrame.
  repartition(1).
  toDF().
  foreachPartition(p => {
    val out = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream("/tmp/temp.xsv.gz")))
    p.foreach(r => {
      val row = ...
      out.write(row)
    })
    val s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.EU_CENTRAL_1).build()
    val tm = TransferManagerBuilder.standard().withS3Client(s3).build()
    val rq = new PutObjectRequest(bucket, key, new File("/tmp/temp.xsv.gz"))
    tm.upload(rq).waitForCompletion()
  })

答案 1 :(得分:0)

好吧,看来我已经开始工作了。根据{{​​3}}的回答,我最终使用了foreach来处理数据,但是由于spark的工作原理,我不得不将数据发送到s3。我还必须使用累加器将json字符串存储在foreach中。

class ArrayAccumulator(AccumulatorParam):
  def zero(self, value):
    return []
  def addInPlace(self, val1, val2):
    val1.extend(val2)
    return val1
jsonAccumulator = sc.accumulator([], ArrayAccumulator())

def write_to_json(row):
  # Process json
  jsonAccumulator += [json]

mappedDF = applymapping1.toDF()
mappedDF.foreach(write_to_json)

count = 0
for x in jsonAccumulator.value:
  s3.Object('bucket-name', 'test/' + str(count) + '.json').put(Body=x)
  count += 1