我们有一个管道,初始阶段可以适当扩展 - 每个人使用几十个工人。
最后阶段之一是
dataFrame.write.format(outFormat).mode(saveMode).
partitionBy(partColVals.map(_._1): _*).saveAsTable(tname)
在这个阶段,我们最终得到了一个单个工作者。这显然不适用于我们 - 实际上工作人员耗尽了磁盘空间 - 而且非常慢。
为什么该命令最终仅在单个worker / single任务上运行?
更新输出格式为parquet
。分区列的数量不会影响结果(尝试了一列和多列)。
以下条件的另一次更新 无(如下面的答案所示):
coalesce
或partitionBy
语句window
/ analytic functions Dataset.limit
sql.shuffle.partitions
答案 0 :(得分:1)
该问题不太可能与saveAsTable
有关。
阶段中的单个任务表示输入数据(Dataset
或RDD
)只有一个分区。这与存在多个任务但一个或多个任务具有明显更高的执行时间(通常对应于包含正偏键的分区)的情况形成对比。另外,您应该混淆CPU利用率低的单个任务方案。前者通常是由于IO吞吐量不足(CPU等待时间较长是最明显的迹象)的结果,但是在极少数情况下,可以追溯到使用具有低级同步原语的共享对象。
由于标准数据源在写入时不会对数据进行混洗(包括使用partitionBy
和bucketBy
选项的情况),因此可以安全地假设数据已在上游代码中重新分区。通常,这意味着发生以下情况之一:
coalesce(1)
或repartition(1)
将数据显式移动到单个分区。数据已隐式移动到单个分区,例如:
Dataset.limit
具有缺少PARTITION BY
子句的窗口定义的窗口函数应用程序。
df.withColumn(
"row_number",
row_number().over(Window.orderBy("some_column"))
)
sql.shuffle.partitions
选项设置为1,并且上游代码包括对Dataset
的非本地操作。
Dataset
是应用全局聚合函数(无GROUP BY
盲注)的结果。除非函数是非归约的(collect_list
或类似的),否则通常这不是问题。虽然没有证据表明这是问题所在,但在一般情况下,您也应该可能,数据仅包含单个分区,一直到源为止。通常在提取using JDBC source时输入,但是第三方格式可以表现出相同的行为。
要确定问题的根源,您应该检查输入Dataset
(explain(true)
)的执行计划,或者检查Spark Web UI的SQL选项卡。