一、为什么要看源码了解其原理呢?(可忽略)

因为项目中需要做排行榜,也就是需要排序,且给出对应排名。

搜索了不少资料,模模糊糊的貌似通过sortBy+zipWithIndex两个算子就能做到。但是就是不敢用。

第一:

不知道sortBy是怎么做到全局排序的,是否有性能问题,导致不太敢用。因为之前学习hadoop的mapreduce,以及hive,了解到全局排序会把所有数据都shuffle到一个reduce里面进行排序(当然,有优化方案)

如果数据量很大,spark也是通过这种方式实现全局排序的话,那spark一个task所需要的内存那会非常大,并且最终只有一个task会需要这么大的内存,那肯定不行。所以由于不清楚spark全局排序的原理,就不敢用,或者想自己实现归并排序。

第二:

不知道zipWithIndex的功能,主要是这个api的文档说的不清楚。

spark指定队列 spark中sortby_数据

如图,圈起来的部分,这个说第一个分区的第一项索引为0,最后一个分区的最后一项为最大的索引。但是否有序?怎么个有序?在zipWithIndex之前执行了sortBy之后,是否会重新shufflue导致打乱顺序?

然后在圈起来的注释下一段又说和scala集合的zipWithIndex是一样的,这就让我觉得这个算子应该是我想像中的那样,会保证顺序,然后按顺序给每个元素递增索引。。

但这不能靠猜测,要么就用大规模数据测试一下,要么就翻源码。

 

我选择了后者,看源码。

 

二、原理

1、sortBy 原理

其实就是使用了RangePartitioner分区器

该分区器的实现方式主要是通过两个步骤来实现的,

(1)、先重整个RDD中抽取出样本数据,将样本数据排序,计算出每个分区的最大key值,形成一个Array[KEY]类型的数组变量rangeBounds;

(2)、判断key在rangeBounds中所处的范围,给出该key值在下一个RDD中的分区id下标

 

这样在分区号大的分区数据就比分区号小的分区数据大了,然后在各个分区内部再分别进行排序,这样的话就全局有序了(按分区号从小到大遍历数据,输出结果就是全局从小到大了)

 

2、zipWithIndex原理

(1)、计算每个分区的数量,然后计算每个分区的第一个元素在全局数据中的位置(index)

(2)、重写getPartitions函数,封装分区数据

正常RDD使用Partition对象存储分区号等信息,而当前zipWithIndexRDD又使用ZippedWithIndexRDDPartition封装进每个分区的第一个元素在全局数据的位置index。

(3)、重写compute函数,RDD会根据分区数n,调用n次该函数。该函数的作用就是重新封装每个分区数据的迭代器。

      参数中,Partition对象又被传进来了,这个对象其实是上面getPartitions函数返回的n多个分区数据之一,也就是说,

这Partition对象其实是ZippedWithIndexRDDPartition类型的实例,所以可以强转类型,获取该分区的第一个元素在全局数据的位置index。

     然后调用getIteratorZipWithIndex函数封装新的迭代器(给每个元素配上该元素在全局数据中的位置),其实就是从该分区一个元素的index开始累加,代码逻辑不复杂。

spark指定队列 spark中sortby_spark_02