spark算子汇总

spark算子汇总

Spark的算子的分类

1)Transformation 变换/转换算子:这种变换并不触发提交作业,完成作业中间过程处理,相当于存储了一段计算逻辑。

Transformation 操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运算。

2)Action 行动算子:这类算子会触发 SparkContext 提交 Job 作业。

Action 算子会触发 Spark 提交作业(Job),并将数据输出 Spark。

1)Value数据类型的Transformation算子

一、输入分区与输出分区一对一型

1、map算子

2、flatMap算子

3、mapPartitions算子

4、glom算子

二、输入分区与输出分区多对一型

5、union算子

6、cartesian算子

三、输入分区与输出分区多对多型

7、grouBy算子

四、输出分区为输入分区子集型

8、filter算子

9、distinct算子

10、subtract算子

11、sample算子

五、Cache型

13、cache算子

14、persist算子

2)Key-Value数据类型的Transfromation算子

一、输入分区与输出分区一对一

15、mapValues算子

二、对单个RDD或两个RDD聚集

单个RDD聚集

16、combineByKey算子

17、reduceByKey算子

18、partitionBy算子

两个RDD聚集

19、Cogroup算子

三、连接

20、join算子

21、leftOutJoin和 rightOutJoin算子

3)Action算子

一、无输出

22、foreach算子

二、HDFS

23、saveAsTextFile算子

24、saveAsObjectFile算子

三、Scala集合和数据类型

25、collect算子

26、collectAsMap算子

27、reduceByKeyLocally算子

28、lookup算子

29、count算子

30、top算子

31、reduce算子

32、fold算子

33、aggregate算子

value类型的Transformation算子:
一、输入分区与输出分区一对一型
    
map算子
将原来 RDD 的每个数据项通过 map 中的用户自定义函数 f 映射转变为一个新的元素。源码中 map 算子相当于初始化一个 RDD, 新 RDD 叫做 MappedRDD(this, sc.clean(f))
    
flatMap算子
对集合中每个元素进行操作然后再扁平化,所以flatMap扁平话意思大概就是先用了一次map之后对全部数据再一次map。
将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的 RDD 的每个集合中的元素合并为一个集合,内部创建 FlatMappedRDD(this,sc.clean(f))
flatMap方法先执行的是map(集合、迭代器来操作),再执行的flatten,flatten起作用的前提是:它之前的map操作使集合中装的元素变成了集合。因此,flatMap内部才经常搭配split使用,正是因为split操作后可以生成集合。

val arr=sc.parallelize(Array(("A",1),("B",2),("C",3)))
arr.flatmap(x=>(x._1+x._2)).foreach(print)
A1B2C3

mapPartitions算子
mapPartitions 函 数 获 取 到 每 个 分 区 的 迭 代器,在 函 数 中 通 过 这 个 分 区 整 体 的 迭 代 器 对整 个 分 区 的 元 素 进 行 操 作。 内 部 实 现 是 生 成MapPartitionsRDD
    
glom算子
    glom函数将每个分区形成一个数组,内部实现是返回的GlommedRDD

在Spark中创建RDD的创建方式大概可以分为三种:(1)、从集合中创建RDD;(2)、从外部存储创建RDD;(3)、从其他RDD创建。
从集合中创建RDD,Spark主要提供了两中函数:parallelize和makeRDD
Spark提供了两种创建RDD的方式:读取外部数据集,以及在驱动器程序中对一个集合进行并行化。
在驱动器程序中对一个集合进行并行化的方式有两种:parallelize()和makeRDD()
parallelize
调用SparkContext 的 parallelize(),将一个存在的集合,变成一个RDD,这种方式试用于学习spark和做一些spark的测试
创建并行集合的一个重要参数,是slices的数目(例子中是numMappers),它指定了将数据集切分为几份。
在集群模式中,Spark将会在一份slice上起一个Task。典型的,你可以在集群中的每个cpu上,起2-4个Slice (也就是每个cpu分配2-4个Task)。
一般来说,Spark会尝试根据集群的状况,来自动设定slices的数目。当让,也可以手动的设置它,通过parallelize方法的第二个参数。
makeRDD
第一种makerdd与parallerize两者完全一致,传递的都是集合的形式;其实第一种makerdd实现是依赖了parallelize函数

def makeRDD[T: ClassTag](
      seq: Seq[T],
      numSlices: Int = defaultParallelism): RDD[T] = withScope {
    parallelize(seq, numSlices)
  }

第二种makerdd还提供了计算位置,分配一个本地Scala集合形成一个RDD,为每个集合对象创建一个最佳分区

def makeRDD[T: ClassTag](seq: Seq[(T, Seq[String])]): RDD[T] = withScope {
    assertNotStopped()
    val indexToPrefs = seq.zipWithIndex.map(t => (t._2, t._1._2)).toMap
    new ParallelCollectionRDD[T](this, seq.map(_._1), math.max(seq.size, 1), indexToPrefs)
  }

sortBy
第一个参数是一个函数,该函数的也有一个带T泛型的参数,返回类型和RDD中元素的类型是一致的;
第二个参数是ascending,从字面的意思大家应该可以猜到,是的,这参数决定排序后RDD中的元素是升序还是降序,默认是true,也就是升序;
第三个参数是numPartitions,该参数决定排序后的RDD的分区个数,默认排序后的分区个数和排序之前的个数相等,即为this.partitions.size。
从sortBy函数的实现可以看出,第一个参数是必须传入的,而后面的两个参数可以不传入。而且sortBy函数函数的实现依赖于sortByKey函数

sortByKey
sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序。它是在org.apache.spark.rdd.OrderedRDDFunctions中实现的
它主要接受两个函数,含义和sortBy一样,这里就不进行解释了。该函数返回的RDD一定是ShuffledRDD类型的,因为对源RDD进行排序,必须进行Shuffle操作,而Shuffle操作的结果RDD就是ShuffledRDD

二、输入分区与输出分区多对一型 
    
union算子
    使用 union 函数时需要保证两个 RDD 元素的数据类型相同,返回的 RDD 数据类型和被合并的 RDD 元素数据类型相同,并不进行去重操作,保存所有元素。如果想去重
可以使用 distinct()。同时 Spark 还提供更为简洁的使用 union 的 API,通过 ++ 符号相当于 union 函数操作
    
cartesian算子
    对 两 个 RDD 内 的 所 有 元 素进 行 笛 卡 尔 积 操 作。 操 作 后, 内 部 实 现 返 回CartesianRDD

zip算子
zip函数用于将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常

zippatitions算子
zipPartitions函数将多个RDD按照partition组合成为新的RDD,该函数需要组合的RDD具有相同的分区数,但对于每个分区内的元素数量没有要求

三、输入分区与输出分区多对多型
    
grouBy算子
将元素通过函数生成相应的 Key,数据就转化为 Key-Value 格式,之后将 Key 相同的元素分为一组

val two: RDD[Int] = sc.parallelize(1 to 9,2)
val a: RDD[(String, Iterable[Int])] = two.groupBy(x=>if(x%2==0)"a" else "b")
===(b,CompactBuffer(1, 3, 5, 7, 9))
===(a,CompactBuffer(2, 4, 6, 8))

groupByKey算子
对Key-Value形式的RDD的操作。
与groupBy类似。但是其分组所用的key不是由指定的函数生成的,而是采用元素本身中的key
groupByKey会对每一个RDD中的value值进行聚合形成一个序列(Iterator),此操作发生在reduce端,所以势必会将所有的数据通过网络进行传输,造成不必要的浪费。同时如果数据量十分大,可能还会造成OutOfMemoryError
当采用groupByKey时,由于它不接收函数,spark只能先将所有的键值对(key-value pair)都移动,
这样的后果是集群节点之间的开销很大,导致传输延时
groupBykey与reduceByKey
在这里插入图片描述

相同点:
1,都作用于 RDD[K,V]
2,都是根据key来分组聚合
3, 默认,分区的数量都是不变的,但是都可以通过参数来指定分区数量
不同点:
1, groupByKey默认没有聚合函数,得到的返回值类型是RDD[ k,Iterable[V]]
2, reduceByKey 必须传聚合函数 得到的返回值类型 RDD[(K,聚合后的V)]
3, groupByKey().map() = reduceByKey
最重要的区别:
reduceByKey 会进行分区内聚合,然后再进行网络传输
groupByKey 不会进行局部聚合

四、输出分区为输入分区子集型
    
filter算子
    filter 函数功能是对元素进行过滤,对每个 元 素 应 用 f 函 数, 返 回 值 为 true 的 元 素 在RDD 中保留,返回值为 false 的元素将被过滤掉。 内 部 实 现 相 当 于 生 成 FilteredRDD(this,sc.clean(f))
    
distinct算子
    distinct将RDD中的元素进行去重操作
    
subtract算子
    subtract相当于进行集合的差操作,RDD 1去除RDD 1和RDD 2交集中的所有元素

val one: RDD[String] = sc.parallelize(List("a","b","c"),2)
val two: RDD[String] = sc.parallelize(List("g","h","s","a"),2)
 one.subtract(two).foreach(x=>println("==="+x)) //结果 b , c
 two.subtract(one).foreach(x=>println("+++"+x))//结果g , h , s

sample算子
    sample 将 RDD 这个集合内的元素进行采样,获取所有元素的子集。
    sample算子是用来抽样用的,其有3个参数
withReplacement:表示抽出样本后是否在放回去,true表示会放回去,这也就意味着抽出的样本可能有重复
fraction :抽出多少,这是一个double类型的参数,0-1之间,eg:0.3表示抽出30%
seed:表示一个种子,根据这个seed随机抽取,一般情况下只用前两个参数就可以,那么这个参数是干嘛的呢,这 个参数一般用于调试,有时候不知道是程序出问题还是数据出了问题,就可以将这个参数设置为定值

takeSample算子
withReplacement:元素可以多次抽样(在抽样时替换)
num:返回的样本的大小
seed:随机数生成器的种子(建议默认不好把控)

五、Cache型
    
cache算子  
使用非序列化的方式将RDD的数据全部尝试持久化到内存中,cache()只是一个transformtion,是lazy的,必须通过一个action触发,才能真正的将该RDD cache到内存中

persist算子
手动选择持久化级别,并使用指定的方式进行持久化
NONE :什么类型都不是
DISK_ONLY:磁盘
DISK_ONLY_2:磁盘;双副本
MEMORY_ONLY: 内存;反序列化;把RDD作为反序列化的方式存储,假如RDD的内容存不下,剩余的分区在以后需要时会重新计算,不会刷到磁盘上。
MEMORY_ONLY_2:内存;反序列化;双副本
MEMORY_ONLY_SER:内存;序列化;这种序列化方式,每一个partition以字节数据存储,好处是能带来更好的空间存储,但CPU耗费高
MEMORY_ONLY_SER_2 : 内存;序列化;双副本
MEMORY_AND_DISK:内存 + 磁盘;反序列化;双副本;RDD以反序列化的方式存内存,假如RDD的内容存不下,剩余的会存到磁盘
MEMORY_AND_DISK_2 : 内存 + 磁盘;反序列化;双副本
MEMORY_AND_DISK_SER:内存 + 磁盘;序列化
MEMORY_AND_DISK_SER_2:内存 + 磁盘;序列化;双副本
*********** 序列化能有效减少存储空间,默认MEMORY_ONLY
Spark会自动地监控每个节点的使用情况,以一种LRU的机制(least-recently-used:最近很少使用)去自动移除。如果想手工代替这种自动去移除,可以使用RDD.unpersist()去处理

2)Key-Value数据类型的Transfromation算子
  一、输入分区与输出分区一对一
key
返回所有键值对的key

val one = sc.parallelize(List(("aa",1),("bb",2),("cc",6)))
one.keys.foreach(x=>println("==="+x))//aa bb cc

values
返回所有键值对的value

 val two = sc.parallelize(List(("aa",3),("dd",4),("aa",5)))
 two.values.foreach(x=>println("==="+x))//3 4 5 

mapValues算子
对键值对每个value都应用一个函数,但是,key不会发生变化

val two = sc.parallelize(List(("aa",3),("dd",4),("aa",5)))
two.mapValues(_+1).foreach(x=>println("---"+x))//(aa,4) (dd,5) (aa,6)

二、对单个RDD或两个RDD聚集
   单个RDD聚集
combineByKey算子
rdd.combineByKey(lambda x:“%d_” %x, lambda a,b:“%s@%s” %(a,b), lambda a,b:“%s$%s” %(a,b))
三个参数(都是函数)
第一个参数:给定一个初始值,用函数生成初始值。
第二个参数:combinbe聚合逻辑。
第三个参数:reduce端聚合逻辑。
在这里插入图片描述
第一个函数作用于每一个组的第一个元素上,将其变为初始值
第二个函数:一开始a是初始值,b是分组内的元素值,比如A[1_],因为没有b值所以不能调用combine函数,第二组因为函数内元素值是[2_,3]调用combine函数后为2_@3,以此类推
第三个函数:reduce端大聚合,把相同的key的数据拉取到一个节点上,然后分组。

reduceByKey算子
    当调用(K,V)对的数据集时,返回(K,V)对的数据集,其中使用给定的reduce函数func聚合每个键
的值,该函数必须是类型(V,V)=> V.
当采用reduceByKeyt时,Spark可以在每个分区移动数据之前将待输出数据与一个共用的key结合
注意在数据对被搬移前同一机器上同样的key是怎样被组合的(reduceByKey中的lamdba函数)。
然后lamdba函数在每个区上被再次调用来将所有值reduce成一个最终结果
在这里插入图片描述
    hello:1 sparkSubmit:1 red:1 sparkSubmit:1 hello:2 hello:1 hello:4 red:1 red:1 red:1 … …
reduceByKey的作用对象是(key, value)形式的RDD,而reduce有减少、压缩之意,reduceByKey的作用就是对相同key的数据进行处理,最终每个key只保留一条记录。
保留一条记录通常有两种结果。一种是只保留我们希望的信息,比如每个key出现的次数。第二种是把value聚合在一起形成列表,这样后续可以对value做进一步的操作
以上面的数据集为例,在spark中比如是word:RDD[(String, Int)] 两个字段分别是word、单个单词在不同文件中出现的次数,现在我们需要统计每个单词出现的总次数。

val word = rdd1.reduceByKey((x,y) => x+y)

简化后

val word= rdd1.reduceByKey(_+_)

reduceByKey会寻找相同key的数据,当找到这样的两条记录时会对其value(分别记为x,y)做(x,y) => x+y的处理,即只保留求和之后的数据作为value。反复执行这个操作直至每个key只留下一条记录。
(1,2),(1,3),(1,8),(2,11),(2,7),(3,2),(3,9)要获得(1,8),(2,7),(3,9),熟悉spark的同学都知道可以通过groupByKey然后取.length-1个可以解决,但是有个更简单的办法,就是用reduceByKey((x,y)=>y),其中y就是最后值。
引申:reduceByKey((x,y)=>x)的x获取的是第一个值

分区三种方式
HashPartitioner确定分区的方式:partition = key.hashCode () % numPartitions
RangePartitioner会对key值进行排序,然后将key值被划分成3份key值集合
CustomPartitioner可以根据自己具体的应用需求,自定义分区
partitionBy算子
该函数根据partitioner函数生成新的ShuffleRDD,将原RDD重新分区,只对K,V类型分区

repartition算子
该函数其实就是coalesce函数第二个参数为true的实现

coalesce算子
将N个分区 合并为 N-M个分区,在filter后使用效果更佳,可以有效避免数据倾斜问题
如果重分区数大于原来的分区数,则必须指定shuffle的参数为true(默认false),否则分区数不变

val one: RDD[(String, Int)] = sc.parallelize(List(("aa",1),("bb",2),("cc",6)),2)
val coalesce1: RDD[(String, Int)] = one.coalesce(3,true)
coalesce1.mapPartitionsWithIndex(fun1).foreach(x=>println("==="+x))
结果: [partid:1, value=(aa,1)]
	  [partid:0, value=(cc,6)]
	  [partid:2, value=(bb,2)]
def fun1(index:Int,itea:Iterator[(String,Int)]):Iterator[String] = {
     itea.toList.map(x => "[partid:" + index +", value="+x+"]" ).iterator
     }

repartitionAndSortWithinPartitions算子
给算子可以通过指定的分区器进行分组,并在分组内排序,shuffle与sort两个操作同时进行,比先shuffle再sort来说,性能可能是要高的
相同组合Key分组到同一分区,分区中先按照KEY排序,KEY相同的情况下按照其他键进行排序。需要创建分区类

mapPartitionsWithIndex
可以按分区,查看里面的数据

val one: RDD[(String, Int)] = sc.parallelize(List(("aa",1),("bb",2),("cc",6)),2)
one.mapPartitionsWithIndex(fun1).foreach(x=>println(x))
[partid:0, value=(aa,1)]
[partid:1, value=(bb,2)]
[partid:1, value=(cc,6)]

def fun1(index:Int,itea:Iterator[(String,Int)]):Iterator[String] = {
     itea.toList.map(x => "[partid:" + index +", value="+x+"]" ).iterator
     }

两个RDD聚集
Cogroup算子
对于每一个k,在other1或者other2里边都可以,返回一个结果RDD,包含了一个元组,元组里面的每一个key,对应每一个other1,other2

val other1 = sc.parallelize(List(("aa",1),("bb",2),("cc",6)))
val other2 = sc.parallelize(List(("aa",3),("dd",4),("aa",5)))
other1.cogroup(other2).foreach(x=>println("==="+x))
===(dd,(CompactBuffer(),CompactBuffer(4)))
===(aa,(CompactBuffer(1),CompactBuffer(3, 5)))
===(bb,(CompactBuffer(2),CompactBuffer()))
===(cc,(CompactBuffer(6),CompactBuffer()))

三、连接
join算子
join、leftOutJoin、 rightOutJoin算子
join算子类似于inner join是根据两个rdd的key进行关联操作,类似scala中的拉链操作,返回的新元素为<key,value>,一对一

3)Action算子
  一、无输出
foreach算子
foreach方法就是在当前节点的内存中完成数据的循环
而算子的逻辑代码是分布式节点(execute)执行的,foreach算子可以将循环在不同的计算节点完成

foreachPartition算子
这个函数也是根据传入的function进行处理,但不同处在于,这里function的传入参数是一个partition对应数据的iterator,而不是直接使用iterator的foreach

二、HDFS
textFile算子
textFile函数为读入一个文件,参数为读取文件的路径

saveAsTextFile算子
saveAsTextFile用于将RDD以文本文件的格式存储到文件系统中
注意:如果使用rdd1.saveAsTextFile(“file:///tmp/lxw1234.com”)将文件保存到本地文件系统,那么只会保存在Executor所在机器的本地目录

saveAsSequenceFile算子
saveAsSequenceFile用于将RDD以SequenceFile的文件格式保存到HDFS上。
用法同saveAsTextFile,对于HDFS,默认采用SequenceFile保存

saveAsObjectFile算子
用于将RDD中的元素序列化成对象,存储到文件中

三、Scala集合和数据类型
first
返回RDD中的第一个元素,不排序

count
返回RDD中的元素数量

reduce
在这里插入图片描述

根据映射函数f,对RDD中的元素进行二元计算,返回计算结果

val one: RDD[Int] = sc.makeRDD(1 to 3,2)
val i: Int = one.reduce(_+_)
println("==="+i)// 6

collect算子
是Action操作里边的一个算子,这个方法可以将RDD类型的数据转化为数组,你可以随时val arr = data.collect(),将RDD类型数据转化为数组来存放并参与后续运算。
已知的弊端
首先,从时间上来讲,前边已经说过了,collect是Action里边的,根据RDD的惰性机制,真正的计算发生在RDD的Action操作。由于collect是从各节点将数据拉到driver端,需要重新分区,所以,一次collect就会导致一次Shuffle,而一次Shuffle调度一次stage,然而一次stage包含很多个已分解的任务碎片Task。这么一来,会导致程序运行时间大大增加,属于比较耗时的操作,即使是在local模式下也同样耗时。
其次,从环境上来讲,本机local模式下运行并无太大区别,可若放在分布式环境下运行,一次collect操作会将分布式各个节点上的数据汇聚到一个driver节点上,而这么一来,后续所执行的运算和操作就会脱离这个分布式环境而相当于单机环境下运行,这也与Spark的分布式理念不合。
最后,将大量数据汇集到一个driver节点上,并且像这样val arr = data.collect(),将数据用数组存放,占用了jvm堆内存,可想而知,是有多么轻松就会内存溢出。
如何规避
若需要遍历RDD中元素,大可不必使用collect,可以使用foreach语句;
若需要打印RDD中元素,可用take语句,data.take(1000).foreach(println),这点官方文档里有说明;
若需要查看其中内容,可用saveAsTextFile方法。
总之,单机环境下使用collect问题并不大,但分布式环境下尽量规避,如有其他需要,手动编写代码实现相应功能就好。

collectpatitions
同样属于Action的一种操作,同样也会将数据汇集到Driver节点上,与collect区别并不是很大,唯一的区别是:collectPartitions产生数据类型不同于collect,collect是将所有RDD汇集到一个数组里,而collectPartitions是将各个分区内所有元素存储到一个数组里,再将这些数组汇集到driver端产生一个数组;collect产生一维数组,而collectPartitions产生二维数组。

collectAsMap算子
collectAsMap对(K,V)型的RDD数据返回一个单机HashMap。 对于重复K的RDD元素,后面的元素覆盖前面的元素。
数据通过collectAsMap函数返回给Driver程序计算结果,结果以HashMap形式存储

reduceByKeyLocally算子
实现的是先reduce再collectAsMap的功能,先对RDD的整体进行reduce操作,然后再收集所有结果返回为一个HashMap

lookup算子
Lookup函数对(Key,Value)型的RDD操作,返回指定Key对应的元素形成的Seq。 这个函数处理优化的部分在于,如果这个RDD包含分区器,则只会对应处理K所在的分区,然后返回由(K,V)形成的Seq。 如果RDD不包含分区器,则需要对全RDD元素进行暴力扫描处理,搜索指定K对应的元素

top算子
top可返回最大的k个元素。
·top返回最大的k个元素。
·take返回最小的k个元素。
·takeOrdered返回最小的k个元素,并且在返回的数组中保持元素的顺序。
·first相当于top(1)返回整个RDD中的前k个元素,可以定义排序的方式Ordering[T]。
返回的是一个含前k个元素的数组

fold算子
fold()与reduce()类似,接收与reduce接收的函数签名相同的函数,另外再加上一个初始值作为第一次调用的结果。(例如,加法初始值应为0,乘法初始值应为1)
fold方法 可以传递2个部分的参数,第一个部分表示集合之外的数据
第二部分的参数表示数据进行的逻辑处理
在这里插入图片描述

val one: RDD[Int] = sc.makeRDD(1 to 3,2)
val i: Int = one.fold(1)(_+_)
println("==="+i)// 9

aggregate算子
def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): U
zeroValue 是一个初始值,自己根据实际情况进行设定;
首先我们知道 RDD 是被分区,然后并行操作的;
seqOp 是对每个分区进行聚合,每个分区聚合结果作为 combOp 的输入;
combOp 对分区聚合结果再次进行聚合;
seqOp 和 combOp 必须有且仅有2个参数

示例如下
seqOp:
把初始值设为 0,累加就是求和
把初始值设为 0,每次加1就是计数;然后 迭代 初始值
combOp:
每个分区的聚合结果为两部分(sum,count)
在初始值的基础上,把每个分区的 sum 相加,count 相加
这两个函数的第一个参数都是累加器,第一次执行时,会把zeroValue赋给累加器。,第一次之后会把返回值赋给累加器,作为下一次运算的第一个参数。seqOP函数每个分区有个累加器,combOp函数只有一个累加器

val one: RDD[Int] = sc.makeRDD(1 to 10,2)
    val i: Int = one.aggregate(0)((x, y) => {
      println(x + " " + y)
0 1
1 2
3 3
6 4
10 5  
0 6     //以为这里设置了两个分区,到元素6时累加器从0开始
6 7
13 8
21 9
30 10
      x + y
    }, (a, b) => {
      println(a + "===" + b)
0===15
15===40
      a + b
    })
    println("+++"+i)
+++55

seqOp对分区内的所有元素遍历计算
这个分区有几个元素执行几次这个方法
当第一个元素1传进来时
(x,y)
x代表累加器,(第一次执行时,会把zeroValue赋给累加器。zeroValue=0),y代表第一个元素1,之后将返回值赋回给累加器。
当第二个元素2传进来时
x代表上一次运算之后赋了新值的累加器,(0+1)=1,y代表传入的第二个元素2
当第三个元素3传进来时
x代表累加器,y代表第新传入的元素…
之后combOp会合并所有分区的结果。
(a,b)
这个函数遍历所有中间结果(累加器:一个分区一个)
第一次执行时,a是combOp累加器(第一次执行时,会把zeroValue赋给累加器),b是第一个分区的累加器,之后将返回值赋回给combOp累加器。
第二次执行时,a是combOp累加器,b是第二个分区的累加器
每次计算后都会把结果赋给combOp累加器作为下一次运算的第一个参数,combOp只有一个累加器。
有几个分区,就执行几次combOp函数。

aggregateByKey
第一个参数是, 给每一个分区中的每一种key一个初始值
第二个是个函数, Seq Function, 这个函数就是用来先对每个分区内的数据按照 key 分别进行定义进行函数定义的操作
第三个是个函数, Combiner Function, 对经过 Seq Function 处理过的数据按照 key 分别进行进行函数定义的操作
也可以自定义分区器, 分区器有默认值
整个流程就是:
在 kv 对的 RDD 中,按 key 将 value 进行分组合并,合并时,将每个 value 和初始值作为 seq 函数的参数,进行计算,返回的结果作为一个新的 kv 对,然后再将结果按照 key 进行合并,最后将每个分组的 value 传递给 combine 函数进行计算(先将前两个 value 进行计算,将返回结果和下一个 value 传给 combine 函数,以此类推),将 key 与计算结果作为一个新的 kv 对输出

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值