目录
一、概述
二、从算子角度理解spark分区
1.Source算子
2.Transformation算子
①repartition&coalease
②groupby & groupbykey &partitionby(new HashPartitioner(num)) & reducebykey... & repartitionAndSortWithinPartitions(new HashPartitioner(10)) & join...
③sortby & sortbykey & partitionby(new RangePartitioner(num,rdd)) & repartitionAndSortWithinPartitions(new RangePartitioner(num,rdd))
3.Action算子
首先,所谓的分区策略,我的理解就是让数据去往哪里。看了很多网上讲spark分区策略,虽然知道了spark有defaultPartitioner、HashPartitioner、RangerPartitioner,但是回到实际工作中还是很懵,比如:啥时候会用到分区策略?用到的时候具体使用的那种策略呢?于是乎,小研究了下,在此总结一下。
一般在日常的工作中,source算子大多数是从例如hdfs、mysql、本地文件等获取数据,这时候数据去往那个分区和分区策略基本没有关系,而是取决于外部的这些系统,比如读取hdfs文件,是根据blocksize以及你设置的分区大小,来计算分区数,然后按照block去读取数据,这个时候数据在哪个分区取决于它属于哪个block;再比如mysql,spark读取mysql一般是单并行度读取,如果是多并行度,数据去往哪个分区取决于你设置的条件;
Transformation算子因为算子的不同分区策略也有所不同,但是总结分为以下几类:
repartition底层是调用开启shuffle的coalease(不讨论不开启shuffle的coalease),当调用他们时,无论是否是key/value类型数据,都是遍历上游算子每个分区中的数据,轮询放入下游算子分区中,放入的时候下游算子的初始分区id是随机的,随后依次加1,源码如下:
这些算子底层都是使用的HashPartitioner,举几个例子:
def groupBy[K](
f: T => K,
numPartitions: Int)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] = withScope {
groupBy(f, new HashPartitioner(numPartitions))
}
def groupByKey(): RDD[(K, Iterable[V])] = self.withScope {
//defaultPartitioner底层也是使用的HashPartitioner
groupByKey(defaultPartitioner(self))
}
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)] = self.withScope {
reduceByKey(new HashPartitioner(numPartitions), func)
}
//join,cartesian,intersection底层都是cogroup实现的
def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))] = self.withScope {
cogroup(other, defaultPartitioner(self, other))
}
从groupby可以看出,不仅key/value数据可以使用HashPartitioner,不是key/value的数据也可以使用,虽然底层是将一条数据作为key,然后调用的groupbykey;
sortby底层是sortbykey,但是其应用的是RangePartitioner
def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
: RDD[(K, V)] = self.withScope
{
val part = new RangePartitioner(numPartitions, self, ascending)
new ShuffledRDD[K, V, V](self, part)
.setKeyOrdering(if (ascending) ordering else ordering.reverse)
}
RangePartitioner的源码还没有读过,但是从测试上来说,相同key还是会被放在同一分区中,他所谓说的尽量均衡应该是指不同key;HashPartitioner是根据key值得hash值%numPartition,从这里来说HashPartitioner可能会更容易造成数据倾斜;
action算子就没有分区策略之说了,要不就是返回给driver端,要不就是写入到外部系统了