使用Spark DataFrame增强联接和组性能

时间:2018-12-21 04:53:49

标签: scala apache-spark dataframe

输入:

我有两个数据集:

  1. samples_1数据集,其中包含以下列:timestamp, id, x, y和500M记录。
  2. samples_2数据集,其列与sample_1相同,并有5000万条记录。

注释:

  • 在单个数据集中,timestampid构成了 每条记录,即timestampid都可以重复。
  • 在所有数据集中,一个数据集中的id无法在另一数据集中复制。不过,timestamp可以在两个数据集中重复。
  • 我的群集包含一个驱动程序节点和五个从属节点,每个从属节点具有16个内核和64 GB的RAM。
  • 我要为我的工作分配15位执行者,每位执行者都有5个内核和19GB的RAM。

问题:

我想做的是:对于(timestamp_1, id_1)中的每个sample_1元组,我需要从(timestamp_2, id_2, x_2, y_2)中找到所有sample_2,其中timestamp_1等于timestamp_2

我尝试过的内容:

samples_2
  .withColumn("combined", struct("id", "x", "y"))
  .groupBy("timestamp")
  .agg(collect_list("combined").as("combined_list"))
  .join(samples_2, Seq("timestamp"), "rightouter")
  .map {
    case Row(timestamp: String, samples: mutable.WrappedArray[GenericRowWithSchema], id_1: String, x_1: Float, y_1: Float) =>
      val overlappingSamples = samples.map {case Row(id_2: String, x_2: Float, y_2: Float) => (id_2, x_2, y_2)}

      if(overlappingSamples.nonEmpty) {
        val stringifiedSamples = overlappingSamples.map(x => s"${x._1}:${x._2}:${x._3}")
        (timestamp, id_1, stringifiedSamples.mkString("&"))
      } else {
        (timestamp, id_1,"", "")
      }

    case Row(timestamp: String, _, id_1: String, x_1: Float, y_1: Float) => // no overlapping samples
      (timestamp, id_1, "", "")

  }
  .write
  .csv(outputPath)

我已经尝试了这段代码(使用较小的数据集),并且给出了我想要的结果。这里的问题是,当我对较大的数据集运行它时,它变得非常慢。我读到我需要通过--conf spark.sql.shuffle.partitions=5000配置分区数,但这并不能解决问题。

1 个答案:

答案 0 :(得分:0)

我在上面的查询中看到的问题是相互绑定的混洗操作太多。我没有检查联接的实际逻辑,但是spark中有一个常见问题需要处理。

我认为,当执行DAG在SPARK中变长时,它几乎不脆弱。原因是第一阶段的任何失败都需要重新计算整个DAG。

我采取的策略是通过保留每次联接的结果来打破多个较小DAG中的DAG或宗族。

val result = datasetA.join(datasetB).persist()
result.count // forces the materialization
// use the result variable in other join
  

在这里,计数是强制性的,就像其他操作一样,持久存储是懒惰的,需要明确的操作(计数)来强制结果的加入和实现。

您可以为您的工作尝试相同的方法并检查性能。