aggregateByKey算子的作用是根据key进行聚合操作,代码均是基于java api,先贴代码:
public class AggregateByKeyOperator { public static void main(String[] args) { SparkConf sparkConf = new SparkConf().setAppName("AggregateOperator") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(sparkConf); List<Tuple2<Integer,Integer>> dataList = new ArrayList<Tuple2<Integer,Integer>>(); dataList.add(new Tuple2<Integer, Integer>(1,99)); dataList.add(new Tuple2<Integer, Integer>(2,78)); dataList.add(new Tuple2<Integer, Integer>(1,89)); dataList.add(new Tuple2<Integer, Integer>(2,3)); dataList.add(new Tuple2<Integer, Integer>(3,3)); dataList.add(new Tuple2<Integer, Integer>(3,30)); JavaPairRDD<Integer, Integer> dataRdd = sc.parallelizePairs(dataList,1); System.out.println("dataRdd.partitions().size():"+dataRdd.partitions().size());
//用于打印每个分区中的数据
dataRdd.mapPartitionsWithIndex(new Function2<Integer, Iterator<Tuple2<Integer, Integer>>, Iterator<Tuple2<Integer, Integer>>>() {
@Override public Iterator<Tuple2<Integer, Integer>> call(Integer index, Iterator<Tuple2<Integer, Integer>> t) throws Exception { System.out.println("partitionId:"+index); while(t.hasNext()) { Tuple2 t2 = t.next(); System.out.println(t2._1+" "+t2._2); } return t; } },true).count(); JavaPairRDD<Integer, Integer> aggregateByKey = dataRdd.aggregateByKey(0,new Function2<Integer, Integer, Integer>() { private static final long serialVersionUID = 1L; @Override
//seqOp public Integer call(Integer t1, Integer t2) throws Exception { System.out.println("seq: " + t1 + "\t " + t2); return Math.max(t1, t2); } },new Function2<Integer, Integer, Integer>() { //combOp private static final long serialVersionUID = 1L; @Override public Integer call(Integer t1, Integer t2) throws Exception { System.out.println("comb: " + t1 + "\t " + t2); return t1+t2; } }); List<Tuple2<Integer,Integer>> resultRdd = aggregateByKey.collect(); for (Tuple2<Integer, Integer> tuple2 : resultRdd) { System.out.println(tuple2._1+"\t"+tuple2._2); } } }
当RDD为一个分区的时候,根据key的不同,可以分为:
key=1: (1,99),(1,89)
key=2: (2,78),(2,3)
key=3:(3,3),(3,30)
这3个组分别进行seqOp,也就是(K,V)里面的V和0进行math.max()运算,运算结果和下一个V继续运算,运算过程是这样的:
key=1: 0,99 =>99,99,89 => 99
key=2: 0,78 => 78,78,3 =>78
key=3: 0,3 =>3,3,30=>30
最终结果是(1,99),(3,30),(2,78) combOp是把不同分区相同的key的V加起来,由于这里并没有分区,所以实际上是不起作用的,计算结果如下:
(1 99)
(3 30)
(2 78)
如果将代码修改为:
JavaPairRDD<Integer, Integer> dataRdd = sc.parallelizePairs(dataList,2);
即指定2个分区,此时分区0的数据为:
1 99
2 78
1 89
分区1的数据为:
2 3
3 3
3 30
则进行seqOp操作时,只需要每个分区内部进行,
seqOp操作后结果,分区0:(1,99),(2,78) 分区1:(2,3),(3,30),根据上面的分析,combOp操作后的最终结果为:
(1,99),(2,81),(3,30)
同理,将分区数修改为3:,三个分区的数据分别为:
partitionId1 :
1 89
2 3
partitionId0 :
1 99
2 78
partitionId2 :
3 3
3 30
seqOp操作后结果为,分区0:(1,99),(2,78),分区1:(1,89),(2,3),分区2:(3,30),则combOp操作后的最终结果为:
(1,188),(2,81),(3,30)
最后总结一下:
aggregateByKey(p1,p2,p3)算子中,p1,p2,p3三个参数,p1为p2中进行比较的初始值,p2中的逻辑是对每一个分区中的key-value键值对进行操作,在本例中是筛选出每一个分区中key对应的最大的value ,p3的逻辑是对不同分区中p2得到的结果进行累加,若只有一个分区,p3无效。