Spark中两个大型数据集之间的交叉联接

时间:2019-01-11 21:19:35

标签: scala apache-spark apache-spark-sql

我有2个大型数据集。 第一个数据集包含约1.3亿个条目。
第二个数据集包含约40000个条目。 从MySQL表中获取数据。

我需要进行交叉联接,但是我得到了

java.sql.SQLException: GC overhead limit exceeded

在Scala中执行此操作的最佳最佳技术是什么?

以下是我的代码段:

val df1 = (spark.read.jdbc(jdbcURL,configurationLoader.mysql_table1,"id",100,100000,40, MySqlConnection.getConnectionProperties))
val df2 = (spark.read.jdbc(jdbcURL,configurationLoader.mysql_table2, MySqlConnection.getConnectionProperties))
val df2Cache = df2.repartition(40).cache()
val crossProduct = df1.join(df2Cache)

df1是较大的数据集,而df2是较小的数据集。

3 个答案:

答案 0 :(得分:4)

130M * 40K = 52万亿条记录是存储此数据所需的52 TB内存,这是假设我们假设每条记录为1个字节,这肯定是不正确的。如果多达64个字节(我认为这也是一个非常保守的估计),则仅需要3.32 PB的内存即可存储数据。这是一个非常大的数目,因此,除非您拥有一个非常大的集群并在该集群内拥有非常快的网络,否则您可能需要重新考虑算法以使其工作。

也就是说,当您对两个SQL数据集/数据帧执行join时,Spark用于存储联接结果的分区数将由spark.sql.shuffle.partitions属性控制(请参见here)。您可能希望将其设置为非常大的数目,并将执行器的数目设置为最大数量。然后,您也许可以将处理过程进行到最后。

此外,您可能需要研究spark.shuffle.minNumPartitionsToHighlyCompress选项;如果将其设置为小于随机播放分区的数量,则可能会增加内存。请注意,在最近的Spark版本之前,此选项是一个硬编码常量,设置为2000,因此根据环境的不同,您只需要将spark.sql.shuffle.partitions设置为大于2000的数字即可使用它。

答案 1 :(得分:1)

同意弗拉基米尔,想增加点数。

请参见MapStatusspark.sql.shuffle.partitions设置为2001old approach)(默认为200)。

弗拉基米尔(Vladimir)在回答中提到的新方法(spark.shuffle.minNumPartitionsToHighlyCompress)。

为什么要更改? : MapStatus has 2000 hardcoded SPARK-24519

它将采用不同的算法进行处理

def apply(loc: BlockManagerId, uncompressedSizes: Array[Long]): MapStatus = {
    if (uncompressedSizes.length > minPartitionsToUseHighlyCompressMapStatus) {
      HighlyCompressedMapStatus(loc, uncompressedSizes)
    } else {
      new CompressedMapStatus(loc, uncompressedSizes)
    }
  }

HighlyCompressedMapStatus

  

一个MapStatus实现,可存储巨大的精确尺寸   块,该块大于spark.shuffle.accurateBlockThreshold。   它存储其他非空块的平均大小以及一个位图   跟踪哪些块为空。

spark.shuffle.accurateBlockThreshold-see here:在HighlyCompressedMapStatus中压缩随机播放块的大小时,如果它超过此配置,我们将准确记录该大小。通过避免在提取随机分组时低估随机分组的大小,有助于防止OOM。


CompressedMapStatus

  

一个MapStatus实现,可跟踪每个块的大小。尺寸   每个块用一个字节表示。

还设置为您的spark-submit

--conf spark.yarn.executor.memoryOverhead=<10% of executor memory>  -- conf spark.shuffle.compress=true --conf spark.shuffle.spill.compress=true 

在两种情况下,压缩将使用spark.io.compression.codec

  

结论 :大型任务应使用HighlyCompressedMapStatus ,执行程序的内存开销可以占执行程序内存的10%。

再看一下spark memory tuning

答案 2 :(得分:0)

将SPARK_EXECUTOR_MEMORY增加到更高的值,然后重新分区到更多分区