使用少量 I/O 的慢速 Spark 阶段

如果你有一个没有多少 I/O 的慢阶段,原因可能是:

  • 读取大量小文件
  • 编写大量小文件
  • 缓慢的 UDF
  • 笛卡尔联接
  • 分解联接

几乎可以使用 SQL DAG 识别所有这些问题。

打开 SQL DAG

若要打开 SQL DAG,请向上滚动到作业页面顶部,然后单击 关联的 SQL 查询

SQL ID

现在应该会看到 DAG。 如果没有,稍微滚动一下,你就能看到它。

SLQ DAG

在继续操作之前,你需要熟悉 DAG 以及花费时间的位置。 DAG 中的某些节点具有有用的时间信息,而其他节点则没有。 例如,此块花费了 2.1 分钟,甚至提供阶段 ID:

慢阶段节点

此节点需要打开它才能看到它花费了 1.4 分钟:

慢写入节点

这些时间是累积的,因此它是所有任务花费的总时间,而不是时钟时间。 但它仍然非常有用,因为它们与时钟时间和成本相关。

了解在 DAG 的哪个部分花费了时间是有帮助的。

读取大量小文件

如果发现某个扫描操作耗费了大量时间,请打开查看读取文件数:

读取多个文件

如果要读取数万个文件或更多文件,则可能有一个小文件问题。 文件不应小于 8MB。 小文件问题通常是通过对过多列或高基数列进行分区引起的。

如果你很幸运,你可能只需要运行 OPTIMIZE。 Databricks 建议还启用 预测优化 并重新考虑 文件布局

编写大量小文件

如果看到写入需要很长时间,请打开它并查找文件数以及写入的数据量:

写入多个文件

如果要编写数万个文件或更多文件,则可能有一个小文件问题。 文件不应小于 8MB。 小文件问题通常是通过对过多列或高基数列进行分区引起的。 需要启用 预测优化、重新考虑 文件布局或打开 优化的写入

UDF 缓慢

如果你知道你有 UDF,或者在 DAG 中看到类似这样的内容,则可能拥有缓慢的 UDF:

UDF 节点

如果你认为你遭遇此问题,请尝试注释掉用户定义函数 (UDF),以查看它对管道速度的影响。 如果 UDF 确实是耗费时间的地方,最佳选择是使用原生函数重写 UDF。 如果无法进行,请考虑执行 UDF 的阶段所涉及的任务数量。 如果它小于群集上的内核数,repartition() 数据帧,然后再使用 UDF。

  (df
    .repartition(num_cores)
    .withColumn('new_col', udf(...))
  )

UDF 也可能因内存问题而受到影响。 请考虑每个任务可能需要将分区中的所有数据加载到内存中。 如果此数据太大,则情况可能会变得非常缓慢或不稳定。 重新分区还可以通过缩小每个任务来解决此问题。

笛卡尔联接

如果在 DAG 中看到笛卡尔联接或嵌套循环联接,则应知道这些联接非常昂贵。 确保这是你打算的,看看是否有另一种方法。

分解联接或分解

如果进入节点的行数很少,而流出的行数却很多,则可能是连接或 explode() 出现了问题:

分解联接

阅读 Databricks 优化指南中有关爆炸的详细信息。