一、概述二、方案适用场景
三、方案实现思路
四、方案实现原理
五、方案优点
六、方案缺点
七、代码实现
一、概述
有的时候,我们可能会遇到大数据计算中一个最棘手的问题--数据倾斜,此时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)