为什么1行的DataFrame上的collect()使用2000个exectors?

时间:2016-06-21 00:40:33

标签: python apache-spark pyspark yarn distributed

这是我能想到的最简单的DataFrame。我正在使用PySpark 1.6.1。

# one row of data
rows = [ (1,   2) ]
cols = [ "a", "b" ]
df   = sqlContext.createDataFrame(rows, cols)

所以数据框完全适合内存,没有引用任何文件,对我来说看起来很微不足道。

然而,当我收集数据时,它使用2000个执行程序:

df.collect()

在收集期间,使用2000个执行程序:

[Stage 2:===================================================>(1985 + 15) / 2000]

然后是预期的输出:

[Row(a=1, b=2)]

为什么会这样? DataFrame不应该完全在驱动程序的内存中吗?

2 个答案:

答案 0 :(得分:3)

所以我稍微研究了一下代码,试图弄清楚发生了什么。似乎sqlContext.createDataFrame确实没有尝试根据数据设置合理的参数值。

为什么要执行2000个任务?

Spark使用2000个任务,因为我的数据框有2000个分区。 (尽管看起来比分行更多的分区似乎是毫无意义的。)

这可以通过以下方式看出:

>>> df.rdd.getNumPartitions()
2000

为什么DataFrame有2000个分区?

这是因为sqlContext.createDataFrame使用默认数量的分区(在我的情况下为2000),无论数据的组织方式如何,或者有多少行。

代码跟踪如下。

sql/context.py中,sqlContext.createDataFrame函数调用(在此示例中):

rdd, schema = self._createFromLocal(data, schema)

反过来调用:

return self._sc.parallelize(data), schema

sqlContext.parallelize功能在context.py中定义:

numSlices = int(numSlices) if numSlices is not None else self.defaultParallelism

未对行数进行检查,并且无法从sqlContext.createDataFrame指定切片数。

如何更改DataFrame的分区数?

使用DataFrame.coalesce

>>> smdf = df.coalesce(1)
>>> smdf.rdd.getNumPartitions()
1
>>> smdf.explain()
== Physical Plan ==
Coalesce 1
+- Scan ExistingRDD[a#0L,b#1L]
>>> smdf.collect()
[Row(a=1, b=2)]

答案 1 :(得分:0)

您可以配置执行程序的数量。在许多情况下,spark会占用尽可能多的执行程序,并且执行时间比限制少量执行程序时要差很多。

conf = SparkConf()
conf.set('spark.dynamicAllocation.enabled','true')
conf.set('spark.dynamicAllocation.maxExecutors','32')