火花错误-批次分辨率达到最大迭代次数(100)

时间:2020-04-09 15:24:59

标签: apache-spark apache-spark-sql data-science

我正在使用Spark SQL,需要找出两个大CSV之间的差异。

差异应提供:-

  • 插入行或新记录//仅比较ID

  • 更改的行(不包括插入的行)-比较所有列值

  • 已删除的行//仅比较ID

Spark 2.4.4 + Java

我正在使用Databricks读取/写入CSV

async function getAllmd5()
{
  const {Storage} = require('@google-cloud/storage');
  const storage = new Storage();
  var bucket = storage.bucket('example');

  var [files] = await bucket.getFiles();

  for (var i = 0; i < files.length; i++)
  {
    console.log(Buffer.from(files[i].metadata.md5Hash, 'base64').toString("ascii"))
  }
}

这对于列数最多为50左右的CSV非常适用。

Dataset<Row> insertedDf = newDf_temp.join(oldDf_temp,oldDf_temp.col(key) .equalTo(newDf_temp.col(key)),"left_anti"); Long insertedCount = insertedDf.count(); logger.info("Inserted File Count == "+insertedCount); Dataset<Row> deletedDf = oldDf_temp.join(newDf_temp,oldDf_temp.col(key) .equalTo(newDf_temp.col(key)),"left_anti") .select(oldDf_temp.col(key)); Long deletedCount = deletedDf.count(); logger.info("deleted File Count == "+deletedCount); Dataset<Row> changedDf = newDf_temp.exceptAll(oldDf_temp); // This gives rows (New +changed Records) Dataset<Row> changedDfTemp = changedDf.join(insertedDf, changedDf.col(key) .equalTo(insertedDf.col(key)),"left_anti"); // This gives only changed record Long changedCount = changedDfTemp.count(); logger.info("Changed File Count == "+changedCount);

但是,如果我的CSV包含300多个列,则它会失败并显示异常

批处理分辨率达到了最大迭代次数(100)–火花错误

The Above code fails for one row in CSV with 300+columns, so I am sure this is not file Size problem.

sparkConf.set(“ spark.sql.optimizer.maxIterations”,“ 500”);

但是我的问题是为什么我必须设置这个?

我在做错什么吗? 或对于具有大列的CSV来说,这种行为是预期的。

我可以通过任何方式对其进行优化以处理大列CSV的

1 个答案:

答案 0 :(得分:2)

您遇到的问题与spark如何接受您告诉它的指令并将其转换为实际要执行的事情有关。它首先需要通过运行Analyzer来了解您的指令,然后尝试通过运行其优化器来改进它们。该设置似乎适用于两者。

特别是您的代码在分析器中的某个步骤中被炸毁。分析器负责弄清您指的是什么东西,实际上是指什么。例如,将函数名称映射到实现,或在重命名和不同的转换之间映射列名称。它会多次执行此操作,每次通过解决其他问题,然后再次检查是否可以解决移动问题。

我认为针对您的情况正在发生的事情是,每个遍可能解析一列,但100次遍还不足以解析所有列。通过增加它,您为它提供了足够的通行证,使其能够完全通过您的计划。这绝对是潜在性能问题的一个危险信号,但是如果您的代码正常运行,那么您可以仅增加该值而不必担心。

如果它不起作用,那么您可能需要尝试做一些事情以减少计划中使用的列数。也许将所有列组合成一个编码的字符串列作为键。在进行联接之前,您可能会受益于对数据进行检查点设置,从而可以缩短计划。

编辑:

此外,我将重构您的上述代码,以便您只需一个连接即可完成所有操作。这应该快很多,并且可能会解决您的其他问题。

每次连接都会导致随机播放(数据在计算节点之间发送),这会增加您的工作时间。您不必一次计算添加,删除和更改,而是可以一次完成所有操作。类似于以下代码。它在scala伪代码中,因为我比Java API更熟悉。

import org.apache.spark.sql.functions._

var oldDf = ..
var newDf = ..
val changeCols = newDf.columns.filter(_ != "id").map(col)

// Make the columns you want to compare into a single struct column for easier comparison
newDf = newDF.select($"id", struct(changeCols:_*) as "compare_new")
oldDf = oldDF.select($"id", struct(changeCols:_*) as "compare_old")

// Outer join on ID
val combined = oldDF.join(newDf, Seq("id"), "outer")

// Figure out status of each based upon presence of old/new
//  IF old side is missing, must be an ADD
//  IF new side is missing, must be a DELETE
//  IF both sides present but different, it's a CHANGE
//  ELSE it's NOCHANGE
val status = when($"compare_new".isNull, lit("add")).
             when($"compare_old".isNull, lit("delete")).
             when($"$compare_new" != $"compare_old", lit("change")).
             otherwise(lit("nochange"))

val labeled = combined.select($"id", status)

在这一点上,我们将每个ID标记为ADD / DELETE / CHANGE / NOCHANGE,因此我们只能对groupBy / count进行分组。此agg几乎可以完全在地图端完成,因此比连接要快得多。

labeled.groupBy("status").count.show