[英]pySpark forEachPartition - Where is code executed
我在 2.3 版中使用 pySpark(在我当前的开发系统中无法更新到 2.4)并且有以下关于foreachPartition的问题。
首先是一点背景:据我了解,pySpark- UDFs
强制 Python 代码在 Python 实例中的 Java 虚拟机 (JVM) 之外执行,从而降低性能。 由于我需要对我的数据应用一些 Python 函数并希望将开销成本降至最低,因此我想到了至少将一组可处理的数据加载到驱动程序中并将其作为 Pandas-DataFrame 进行处理。 无论如何,这将导致 Spark 失去并行优势。 然后我读到foreachPartition
将函数应用于分区内的所有数据,因此允许并行处理。
我现在的问题是:
当我通过foreachPartition
应用 Python 函数时,Python 执行是否发生在驱动程序进程中(分区数据因此通过网络传输到我的驱动程序)?
数据是在foreachPartition
中按行处理的(意味着每个 RDD 行都被一个一个地传输到 Python 实例),还是一次处理分区数据(意味着,例如,整个分区被传输到实例并由一个 Python 实例整体处理)?
预先感谢您的输入!
编辑:
我之前使用的驱动程序解决方案看起来像这样,取自 SO here :
for partition in rdd.mapPartitions(lambda partition: [list(partition)]).toLocalIterator():
# Do stuff on the partition
从文档中可以看出, rdd.toLocalIterator()
提供了必要的功能:
返回包含此 RDD 中所有元素的迭代器。 迭代器将消耗与该 RDD 中最大分区一样多的内存。
幸运的是,我偶然发现了 Mrinal 对mapPartitions
的精彩解释( 在此处回答)。
mapPartitions
在 RDD 的每个分区上应用一个函数。 因此,如果分区分布在不同的节点上,则可以使用并行化。 在这些节点上创建处理 Python 函数所必需的相应 Python 实例。 虽然foreachPartition
仅应用一个函数(例如,将您的数据写入 a.csv 文件), mapPartitions
还返回一个新的 RDD。 因此,使用foreachPartition
对我来说是错误的选择。
为了回答我的第二个问题:像map
或UDFs
这样的函数会创建一个新的 Python 实例,并逐行传递来自 DataFrame/RDD 的数据,从而导致大量开销。 foreachPartition
和mapPartitions
(均为 RDD 函数)将整个分区传输到 Python 实例。
此外,使用生成器还减少了迭代此传输的分区数据所需的内存量(分区作为迭代器对象处理,而每一行然后通过迭代此对象来处理)。
一个示例可能如下所示:
def generator(partition):
"""
Function yielding some result created by some function applied to each row of a partition (in this case lower-casing a string)
@partition: iterator-object of partition
"""
for row in partition:
yield [word.lower() for word in row["text"]]
df = spark.createDataFrame([(["TESTA"], ), (["TESTB"], )], ["text"])
df = df.repartition(2)
df.rdd.mapPartitions(generator).toDF(["text"]).show()
#Result:
+-----+
| text|
+-----+
|testa|
|testb|
+-----+
希望这可以帮助面临类似问题的人:)
pySpark UDF 在执行器附近执行——即在一个独立的 python 实例中,每个执行器并排运行并在 spark 引擎 (scala) 和 python 解释器之间来回传递数据。
对于在 foreachPartition 中调用 udfs 也是如此
编辑 - 查看示例代码后
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.