繁体   English   中英

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

[英]Cross join between two large datasets in Spark

我有 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 是较小的数据集。

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

话虽如此,当您join两个 SQL 数据集/数据帧时,Spark 用于存储连接结果的分区数由spark.sql.shuffle.partitions属性控制(请参阅此处)。 您可能希望将其设置为一个非常大的数字,并将执行程序的数量设置为您能做到的最大数量。 然后,您也许可以将处理运行到最后。

此外,您可能需要查看spark.shuffle.minNumPartitionsToHighlyCompress选项; 如果您将其设置为少于 shuffle 分区的数量,您可能会再次获得内存提升。 请注意,此选项是一个硬编码常量,直到最近的 Spark 版本才设置为 2000,因此根据您的环境,您只需将spark.sql.shuffle.partitions设置为大于 2000 的数字即可使用它。

同意 Vladimir 的观点,想加分。

看到MapStatusspark.sql.shuffle.partitions2001旧办法)(默认值是200)。

新方法( spark.shuffle.minNumPartitionsToHighlyCompress )正如 Vladimir 在回答中提到的那样。

为什么会有这种变化? MapStatus 有 2000 个硬编码 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 - 看这里:当我们在HighlyCompressedMapStatus压缩 shuffle 块的大小时,如果它高于此配置,我们将准确记录大小。 这有助于通过在获取 shuffle 块时避免低估 shuffle 块大小来防止 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_EXECUTOR_MEMORY 增加到更高的值并重新分区到更多分区

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM