一、概述二、方案适用场景
三、方案实现思路
四、方案实现原理
五、方案优点
六、方案缺点
七、代码实现

一、概述

有的时候,我们可能会遇到大数据计算中一个最棘手的问题--数据倾斜,此时Spark作业的性能会比期望差很多。数据倾斜调优,就是使用各种技术方案解决不同类型的数据倾斜问题,以保证Spark作业的性能。

二、方案适用场景

在对RDD使用join类操作,或者是在Spark SQL中使用join语句,而且join操作中的一个RDD或表的数据量比较小(几百M或一两G),比较适合此方案。

三、基本实现思路:

不使用join算子进行连接操作,而使用Broadcast变量与map类算子实现join操作,进而完全规避掉shuffle类的操作,彻底避免数据倾斜的发生。
将较小的RDD中的数据直接通过collect算子拉取到Driver端的内存中来,然后对其创建一个Broadcast变量;
接着对另外一个RDD执行map类算子,在算子函数中,从Broadcast变量中获取较小RDD的全量数据,与当前RDD的每一条数据按照连接key进行比对,如果连接key相同的话,那么就将两个RDD的数据用你需要的方式连接起来。

四、方案实现原理

普通的join是会走shuffle过程的,而一旦shuffle,就相当于会将相同key的数据拉取到一个shuffle read task 中再进行join,此时就是reduce join。 但是如果一个RDD是较小的,则可以采用广播小RDD的全量数据+map算子来实现与join同样的效果,此时不产生shuffle操作,也就规避了数据倾斜。

五、方案优点

对join操作导致的数据倾斜,效果非常好,因为根本就不会发生shuffle,也就根本不会发生数据倾斜。

六、方案缺点

只适用于一个大表和一个小表的情况。我们需要将小表进行广播,此时会消耗内存资源,driver和每个Executor内存中都会驻留一份小RDD的全量数据。如果我们广播出去的RDD数据较大,比如10G以上,那么就可能发生内存溢出了,所以不适用于两个都是大表的情况。
  • 如果对于hive中的map join熟悉的同学看到这幅图应该很好理解,所以建议大家看看hive中的map join原理,进行学习。

七、代码实现:

object BroadCastApp{
		def main(args: Array[String]: Unit = {
				val conf = new SparkConf().setAppName("BroadCastApp").setMaster("local[2]")
				val sc = new SparkContext(conf)

			val smallRDD = sc.parallelize(Array(("1","ruoze"),("2","jepson"),("3","xingxing"))).collectAsMap()

			val smallBroadCast = sc.broadcast(smallRDD)
			
			val bigRDD = sc.parallelize(Array(("1","school01","male"),("2","school02","female"),("3","school03","male"),("4","school04","female"),("5","school05","male"))).map( x => (x._1,x))
			
			val broadCastValue = smallBroadCast.value
			
			bigRDD.mapPartitions(partitions => {
					for ( (key,value)  <- partitions
					if(broadCastValue.contains(key)))
					yield(key,broadCastValue.getOrElse(key,""),value._2,value._3)
		}).collect().foreach(println)

	}
}

输出信息如下:

(1,ruoze,school01,male)
(2,jepson,school02,female)
(3,xingxing,school03,male)