java实现spark

来源:互联网 发布:电音入门耳机 知乎 编辑:程序博客网 时间:2024/06/06 17:33

这篇博客简要回顾一下我对spark的认识,主要记录的是对spark的两个转换操作aggregate() combineByKey()的运用。下载配置完spark后,注意要把spark的jar导入项目中。

在spark中两个重要的概念就是RDD和操作。
RDD也即弹性分布式数据集。是一个不可变的分布式对象集合。简单地理解,就把它看成数据的容器就好了,我们所有的操作都是在这上面进行的。
除了数据容器我们还需要对数据进行操作,spark中的操作分为两种:转换和行动。顾名思义,转换就是从一个RDD转变成另一个RDD,但是要注意,spark的惰性操作机制,即你所有的转换操作都不会立即执行,它只是“记下来”了而已,只有当遇见行动操作,spark才进行真刀真枪的计算。

现在就让我们来看看在这两个基础上我们能做什么吧。

首先,如何把数据变成RDD呢?

读取一个外部数据,或者在驱动器中对一个集合进行并行化。

JavaRDD<String> line = sc.textFile("README.md");JavaRDD<String> words = sc.parallelize(Array.asList(["pandas", "orange"]));

 操作

spark有很多转换操作,最常用的包括:map() flatMap() filter()
map(function) 即是对RDD中的每一个数据使用括号中的方法function
方法的写法有两种:匿名函数或者具名函数,比如
(哦,对了,方法最好声明为静态,我也不知道为什么,没有声明为静态的时候方法就用不了,一直在报错)

JavaRDD<Integer> rdd = sc.parallelize(Array.asList(1,2,3,4,5));匿名函数:对rdd中的每一个数字都使用括号中的方法JavaRDD<Integer> doubleRDD = rdd.map(new Function<Integer, String>(){    public Integer call(Integer x){return x * x;}})具名函数:具名的优点是可以把方法与rdd操作分开,便于修改。另外,还可以传参static class doubleClass implements Function<Integer, Integer>(){    int initNum ;    public doubleClass(int num){this.initNum = num;}    public Integer call(int x){ return initNum * x * x;}}JavaRDD<Integer> newRDD = rdd.map(new doubleClass(5));

无论是具名还是匿名,在java中,函数都需要实现接口,比如一下几个常见接口,接口的使用取决于操作类型类型和参数多少,比如,fold()操作是接受两个参数(T1,T2)并返回一个数(R)的,匹配Function2。

函数名 实现的方法 用途 Function< T,R > R call(T) 接受一个类型为T的输入值,返回一个类型为R的输出值,用于类似map() filter()等操作中 Function2< T1,T2,R > R call(T1, T2) 接受两个输入值(T1,T2),返回一个输出值(类型为R),用于类似aggregate() fold()等方法中 FlatMapFunction< T, R > Iterable< R > call (T) 接受一个输入值并返回任意个输出值用于类似flatMap()操作中

filter(function) 用于过滤,只有function返回true时数据才被保留,所以我们写function时也应该定义其返回值为布尔。

reduce(function) 操作一次接收两个相同的元素并返回一个同类型的元素,很适合用于累加的情况。

注意接口的使用,参数的对应。JavaRDD<Integer> addAllRDD = rdd.reduce(new Function2<Integer, Integer,Integer>(){    public Integer call(Integer x, Integer y){return x + y;}})

该方法将会把第一个数和第二个数相加的结果作为下次运算的第一个参数,直到结束。

如果你发现你使用了map()操作后又使用了reduce(),或者你希望返回值类型不同于操作类型,也许你应该想到aggregate()操作,该方法将我们从操作类型和返回类型必须相同的限制中解救出来,灵活运用,我们可以创建自己所需的RDD类型。
比如说,我现在有的RDD中每一个元素是一个二元组:Tuple2< String, Integer[] >,我希望累加所有数组对应下标的数字,而不关心字符串(已知数组长度为4)。可以这么写

int[] array = pairRDD.aggregate(initial, seqOp, combOp);

下面解释三个参数,initial是初始值,也即期待返回的类型

int[] initial = new int[]{0, 0, 0, 0};

seqOp则是定义如何把原始数据变成期待类型方法

Function2<int[], Tuple2<String, Integer>, int[]>seqOp =     new Function2<int[], Tuple2<String, Integer>, int[]>(){        public int[] call(int[] array, Tuple2<String, Integer[]> tup){            for(int i = 0; i < array.length; i ++)                array[i] += tup._2[i];            return array;        }    }

combOp则定义的是不同分区的累加器如何相加。可以想象,spark把数据放在不同分区进行计算,当一个分区所有的二元组都已经变成数组后,还要设置一个不同分区的数组相加的计算方法,才能达到累加原始数据数组的目标。这样,也就不难写出函数的接口了。

Function2<int[], int[], int[]>combOp =     new Function2<int[], int[], int[]>(){        public int[] call (int[] one, int[] two){            for(int i = 0; i < one.length; i ++) one[i] += two[i];            return one;        }}

我们也可以创建一个新类,并new一个实体作为初始值,灵活使用,aggregate()操作可以为我们带来很多方便。
spark提供很多操作,包括对集合的去重,取并,取交等操作,详细的请看官方文档,这里不一一介绍。

spark中还有一类常用RDD,键值对RDD(pairRDD),存储诸如【键:值】的数据。通常用mapToPair()方法把一个普通RDD转换成pairRDD,键值对操作又有各自新的接口。

在java中使用scala的二元组Tuple2< k,v >来表示一个键值对。通过tuple._1, tuple._2的方法取得键、值。

根据键进行的运算有reduceByKey() :累加相同键的值,groupByKey():聚合相同键的值。这里想讲一个与普通RDD 操作aggregate有点异曲同工之处的操作combineByKey()

在项目中,我现有的RDD每个数据类型如下,比如【“苹果”,int[]{ 1, 2 }】,【“香蕉”, int[]{4, 1}】,【“苹果”,int[]{ 3, 9} 】,我希望把键相同的,数组对应下标累加,即苹果的值应该是int[]{ 4, 11}。
首先我创建一个类

            class score implements Serializable{                public int positive;                public int negative;                public score(int one, int two){                    this.positive = one;                    this.negative = two;                }            }

定义原始数据如何转变为新类

            Function<Integer[], score> createScore = new Function<Integer[], score>(){                public score call(Integer[] array){                    return new score(array[0], array[1]);                }            };

写一个方法,当我的初始值或者是那些已经转换后的类型遇到原始数据时:

            Function2<score, Integer[], score> addAndCount = new Function2<score, Integer[], score>(){                public score call(score scoreClass, Integer[] array){                    return new score(scoreClass.positive + array[0], scoreClass.negative + array[1]);                }            };

还要考虑到不同分区,整合后的第一个分区和第二个分区,【“苹果”,score】要如何累加

            Function2<score, score, score> combine = new Function2<score, score, score>(){                public score call(score one, score two){                    return new score(one.positive + two.positive, one.negative + two.negative);                }            };

最后JavaPairRDD<String, score> timeScoreClassPairRDD = timeScorePairRDD.combineByKey(createScore, addAndCount, combine)
图解如下:
combineByKey

0 0