Spark full outer join 数据倾斜导致OOM

Spark在执行FullOuterJoin时,遇到数据倾斜可能导致Executor内存溢出(OOM)。问题主要出在SortMergeFullOuterJoinScanner类中,它在内存中存储匹配的左右两侧数据,而没有设定内存限制。当匹配数据量过大时,会消耗过多内存,引发OOM。解决方案可能涉及优化数据分布或调整Join策略。

spark full outer join目前存在一个问题,那就是在数据倾斜的时候,会导致Execuotr OOM:具体的问题描述,可以见SPARK-24985,
转述一下就是:
SortMergeJoinExec类以下代码块的处理:

doExecute 
   ||
   \/
case FullOuter =>
     val leftNullRow = new GenericInternalRow(left.output.length)
     val rightNullRow = new GenericInternalRow(right.output.length)
     val smjScanner = new SortMergeFullOuterJoinScanner(
     leftKeyGenerator = createLeftKeyGenerator(),
     rightKeyGenerator = createRightKeyGenerator(),
     keyOrdering,
     leftIter = RowIterator.fromScala(leftIter),
     rightIter = RowIterator.fromScala(rightIter),
     boundCondition,
     leftNullRow,
     rightNullRow)

     new FullOuterIterator(
      smjScanner,
      resultProj,
      numOutputRows).toScala

其中SortMergeFullOuterJoinScanner在迭代的时候对左右两边匹配的数据保存在内存中,这个内存是没有边界的,具体的处理方法如下:

private def findMatchingRows(matchingKey: InternalRow): Un
### Spark Join 算子的使用方法与示例 在 Spark 中,`join` 是一种常见的操作,用于将两个分布式集合(RDD 或 DataFrame)按照相同的键进行连接。以下是关于 `join` 算子的核心概念、分类以及具体的使用方法。 --- #### 1. **核心概念** - **Join**:将两个数据集中的记录按指定条件关联起来。 - **Key-Based Operation**:基于键的操作,要求两个数据集中存在共同的键字段。 Spark 提供了多种类型的 `join` 操作,包括但不限于内部连接 (`inner join`)、外部连接 (`outer join`) 和左/右半连接 (`left/right semi join`)。 --- #### 2. **RDD 上的 Join** ##### (1)基本语法 对于 RDD 数据结构,`join` 的基本形式如下: ```scala def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))] ``` 其中: - 输入为两个 RDD,分别表示 `(key, value)` 形式的键值对。 - 输出是一个新的 RDD,其值由两个输入 RDD 中对应键的组合构成。 --- ##### (2)代码示例 以下展示了如何在 Scala 中使用 `join` 进行简单连接: ```scala val rdd1 = sc.parallelize(Seq(("a", 1), ("b", 2), ("c", 3))) val rdd2 = sc.parallelize(Seq(("a", 4), ("b", 5))) // 执行 inner join val joinedRDD = rdd1.join(rdd2) joinedRDD.collect().foreach(println) /* 输出结果: (a,(1,4)) (b,(2,5)) */ ``` 此示例中,只有键 `"a"` 和 `"b"` 同时存在于两个 RDD 中,因此它们被保留下来并组成一个新的 RDD[^4]。 --- #### 3. **DataFrame 上的 Join** 相比于 RDD,DataFrame 提供了更高级别的抽象和更简洁的接口来完成各种类型的连接操作。 ##### (1)基本语法 在 DataFrame 中,`join` 方法的一般形式为: ```scala def join(right: Dataset[_], usingColumns: Seq[String], joinType: String): DataFrame ``` 参数说明: - `right`: 表示要连接的右侧表。 - `usingColumns`: 列名数组,指明哪些列作为公共键参与连接。 - `joinType`: 字符串类型,定义连接的具体种类(如 `"inner"`, `"left_outer"`, `"right_outer"`, `"full_outer"` 等)。 --- ##### (2)代码示例 下面展示了一些典型的 DataFrame 加入案例: ```scala import org.apache.spark.sql.SparkSession val spark = SparkSession.builder.appName("Example").getOrCreate() // 创建第一个 DataFrame val dataFrame1 = Seq( ("Alice", 1), ("Bob", 2) ).toDF("name", "id") // 创建第二个 DataFrame val dataFrame2 = Seq( (1, "Engineer"), (2, "Doctor") ).toDF("id", "profession") // Inner Join val innerJoinedDF = dataFrame1.join(dataFrame2, Seq("id"), "inner") innerJoinedDF.show() /* +---+-----+----------+ | id| name| profession| +---+-----+----------+ | 1|Alice| Engineer | | 2| Bob | Doctor | +---+-----+----------+ */ // Left Outer Join val leftOuterJoinDF = dataFrame1.join(dataFrame2, Seq("id"), "left_outer") leftOuterJoinDF.show() /* 假设 dataFrame2 缺少一些匹配项,则显示 null 值 */ // Full Outer Join val fullOuterJoinDF = dataFrame1.join(dataFrame2, Seq("id"), "full_outer") fullOuterJoinDF.show() /* 即使某一方缺少匹配项也会全部展现出来 */ ``` 这些不同的连接方式可以根据实际需求灵活选用[^3]。 --- #### 4. **注意事项** - **Shuffle 开销**: 如果涉及大规模数据集间的连接操作,可能会触发大量的 shuffle,进而影响性能。可以通过调整分区策略或者预处理数据减少不必要的移动成本。 - **内存消耗**: 特别是在执行宽依赖型作业时需要注意资源分配情况以免发生 OOM 错误。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值