Spark:处理所有特定RDD或DataFrame分区的数据

时间:2016-12-06 22:56:39

标签: apache-spark pyspark apache-spark-sql partitioning pyspark-sql

我在文档中发现了几篇帖子,文章,参考资料,暗示了您可以使用foreachPartition访问特定分区的想法。但是我还没弄明白如何对给定分区中的所有数据做些什么。

我的目标是从数据库中选择一些数据,对其进行操作,按列中的唯一值进行分区,然后将每个分区作为一个特定名称的jsonl文件写入s3,以供另一个系统访问。

repartitioned = myDataframe.repartition("processed_date")
repartitioned.foreachPartition(writePartitionToS3)

我已经尝试了很多方法来解析这些数据,但似乎我只能在foreachPartition中获得单个元组,并且没有对分区本身进行限制,以便有效地分离这些数据。

def writePartitionsToS3(partition):
    for row in partition:
        pprint (row)

生成(为简洁起见删除了几列):

  

行(entity_id = u'2315183',... processed_date = datetime.date(2015,3,   25))行(entity_id = u'2315183',... processed_date = datetime.date(2015,   3,25))行(entity_id = u'2315183',...   processed_date = datetime.date(2015,3,25))Row(entity_id = u'2315183',   ... processed_date = datetime.date(2015,3,25))

也可能没有像我认为的那样定义分区,但我知道有一个内置的DataFrameWriter可以通过分区编写,但我不能使用它。我真的需要能够生成这样的命名文件,而不是part-xxx格式:

s3a://<bucket>/<prefix>/<date processed>.jsonl

我正在以这样的方式分块数据:分区的大小相对较小(每个被处理的日期一个,每个实体选择为它自己的DataFrame),所以这不是问题,但我也不是真的想要到collect()一个节点上的所有内容都要解析分区列表,因为我想将文件并行写入s3。

更新

我最终通过获取唯一值然后根据这些数据集过滤原始数据集来实际解决我的问题。请记住,如果数据集非常大,您永远不会想要这样做,但我可以选择,因为我在循环中创建小数据帧(从数据库中选择),然后处理这些块。

# Get a list of the unique values present
# in the processed_date column
uniqueProcessedDates = myDataframe.select('processed_date') \
    .distinct().rdd.map(lambda r: r[0]).collect()

# For each unique processed date we want to
# filter records and then write them
for value in uniqueProcessedDates:
    sortedRowsThisProcessedDate = myDataframe.filter(postgresDF.processed_date == date)

    # some custom function to write the data
    writeProcessedDatesToS3(sortedRowsThisProcessedDate.collect())

所有人都说过,我确信有很多方法可以解决这个问题。我正在考虑的一件事是通过需要写入每个文件的确切值集来重新划分每个RDD,因为对s3的写入必须以原子方式完成。我认为增加这一点可能有助于避免在写入数据之前从多个节点收集。

1 个答案:

答案 0 :(得分:1)

无法访问。 DataFrame.repartition使用散列分区器来对数据进行混洗,因此行的同时发生没有更广泛的含义。您可以在此假设,特定processed_date的所有记录都位于特定分区上。

您可以通过添加sortWithinPartitions

来改善情况
(myDataframe
    .repartition("processed_date")
    .sortWithinPartitions("processed_date"))

能够逐个访问单个日期的所有记录。

另一个可能的改进是使用orderBy方法:

myDataframe.orderBy("processed_date")

这将导致连续的日期,但仍然无法访问边界。

在这两种情况下,您都必须在迭代分区时手动检测边界。

最后,如果您想要真正的控件,请使用RDDrepartitionAndSortWithinPartitions方法。这将为您提供对数据分布的精细控制。您可以定义partitionFunc以特定方式分发数据,因此不会预先设置分区边界。

DataFrameWriter.partitionBy使用不同的机制,这对你没用。