解决Spark中出现的数据倾斜
使用重写UDAF完成Map端聚合来解决
业务背景:
为了求城市的热门栅格的相关属性 需要关联用户拉链表和城市栅格表来做分析
但是热门栅格的人数和非热门栅格的差别过大会导致数据倾斜
具体实现:
package com.exmaple
import org.apache.spark.sql.Row
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types._
class ActionMapUDAF extends UserDefinedAggregateFunction {
override def inputSchema: StructType = {
// 定义输入格式
StructType(
StructField("action_datetime", StringType) ::
StructField("action_info", StringType) :: Nil)
}
override def bufferSchema: StructType = {
// 定义存储中间结果的状态格式
StructType(StructField("hashmap", MapType(StringType, StringType)) :: Nil)
}
override def dataType: DataType = {
// 最终的输出格式
MapType(StringType, StringType)
}
override def deterministic: Boolean = {
// 对给定的一组输入值,UDAF的输出值是否相同
true
}
override def initialize(buffer: MutableAggregationBuffer): Unit = {
// 初始化
buffer(0) = Map()
}
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
// Reduce的更新操作:如何根据一行新数据, 更新一个聚合buffer的中间结果
val map1 = buffer.getAs[Map[String, String]](0)
val key = input.getAs[String](0)
val value = input.getAs[String](1)
buffer(0) = map1 + (key -> value)
}
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
// Reduce的merge操作:两个buffer结果合并到其中一个bufer上
var mp1 = buffer1.getAs[Map[String, String]](0)
var mp2 = buffer2.getAs[Map[String, String]](0)
buffer1(0) = mp1 ++ mp2.map{ case (k, v) => k -> (v + "," + mp1.getOrElse(k, "")) }
}
override def evaluate(buffer: Row): Any = {
// 最终的结果
buffer.getAs[Map[String, String]](0)
}
}
问题一:这种处理类似于Hive的Mapjoin 那么这个UDAF会导致OOM吗?
我们知道Hive的MapJoin是把小表一次性的全部读取到内存当中来进行处理 因为堆内存有限所以当表数据量大的时候会造成OOM
但是Spark在使用这个UDAF之前,一定是已经把数据读取读取到了栈空间内了 然后再进行聚合 这样会压缩数据的规模
当然 因为聚合离不开状态的存在 而状态一定会驻留在内存当中 如果这时遇到了OOM问题该如何处理?
方法一:增加读入数据的分区数 让每个Executor读取的数据减少 降低OOM的风险
方法二:增加执行内存的大小