Spark中有许许多多的算子来支持各种操作,但其中主要分为两种;一种就是Transformations算子,一种是Action算子。官方文档中已经指出了常用的算子。好记性不如烂笔头,在这里记录一下各个算子的作用以及使用方法。
Transformations算子:顾名思义,这种算子的作用就是将一个RDD转换成另一种RDD,有的算子转换过程中还会涉及到parition的变化和Shuffle操作,这里只介绍算子的使用方法,其中的parition和shuffle的具体变换可能不会提到。
Action算子:该算子会触发一个runJob操作,也就是只有使用了Action算子才会将程序提交到集群进行计算,最后会得到一个结果。
SparkConf和JavaSparkContext的初始化。
SparkConf conf = new SparkConf().setMaster("local").setAppName("TransformationsOperator");
JavaSparkContext sc = new JavaSparkContext(conf);
1 . map(func)。
官方介绍:
Return a new distributed dataset formed by passing each element of the source through a function func.
通过函数将RDD中的每个元素进行转换形成一个新的RDD。
操作示例:
// map,一次只处理一个parition中的一条数据。
private static void MapOperator(JavaSparkContext sc) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
JavaRDD<Integer> numberRDD = sc.parallelize(numbers);// 得到一个RDD
JavaRDD<String> results = numberRDD.map(new Function<Integer, String>() {
//使用map操作将Integer类型转换成String
private static final long serialVersionUID = 1L;
@Override
public String call(Integer number) throws Exception {
return "number:" + number;
}
});
results.foreach(new VoidFunction<String>() {
private static final long serialVersionUID = 1L;
@Override
public void call(String arg0) throws Exception {
System.out.println(arg0);
}
});
}
2.mapPartitions(func)。
官方介绍:
Similar to map, but runs separately on each partition (block) of the RDD, so func must be of type Iterator => Iterator when running on an RDD of type T.
作用于map一致,不过是以每个parition作为一个操作单位的,所以返回类型是一个Iterator。
操作示例:
// mapPartitions,这个是针对Partition的操作,一次会处理一个partition的所有数据
private static void MapPartitionsOperator(JavaSparkContext sc) {
List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu");
JavaRDD<String> nameRDD = sc.parallelize(names,2);
final Map<String, Integer> scoreMap = new HashMap<>();
scoreMap.put("zhangsan", 100);
scoreMap.put("lisi", 99);
scoreMap.put("wangwu", 98);
//这里会使用FlatMapFunction将Iterator中的数据自动压缩成Integer数据。
JavaRDD<Integer> scoreRDD = nameRDD.mapPartitions(new FlatMapFunction<Iterator<String>, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public Iterator<Integer> call(Iterator<String> iterator) throws Exception {
List<Integer> scores = new ArrayList<>();
while (iterator.hasNext()) {
String name = iterator.next();
int score = scoreMap.get(name);
scores.add(score);
}
return scores.iterator();
}
});
scoreRDD.foreach(new VoidFunction<Integer>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Integer score) throws Exception {
System.out.println(score);
}
});
}
可以通过FlatMapFunction的参数看到,第一个是Iterator< String>的,也就是输入的数据是一个Iterator,输出的是Integer,这个输入的 Iterator就是将一个partition中的所有数据传入进来,经过操作后变成一个Iterator< Integer>的,然后在自动压缩成Integer。
3 . mapPartitionsWithIndex(func)。
官方介绍:
Similar to mapPartitions, but also provides func with an integer value representing the index of the partition, so func must be of type (Int, Iterator) => Iterator when running on an RDD of type T.
与上述的mapParitions神似,不过每次调用call函数的时候会传入一个当前parition的下标进来。
操作示例:
// 可以看到使用了哪一个parition,采用分区的话:parallelize优先级最高,其次conf.set,最后时local[];
private static void MapPartitionsWithIndexOperator(JavaSparkContext sc) {
List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu");
JavaRDD<String> nameRDD = sc.parallelize(names, 2);//这里加载的数据设置成2个partition。
JavaRDD<String> results = nameRDD
.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {
private static final long serialVersionUID = 1L;
//这里会有一个Integer的index,可以通过这个来查看当前操作属于哪一个parition。
@Override
public Iterator<String> call(Integer index, Iterator<String> names) throws Exception {
List<String> nameList = new ArrayList<>();
while (names.hasNext()) {
String name = names.next();
name = index + ":" + name;
nameList.add(name);
}
return nameList.iterator();
}
}, true);
results.foreach(new VoidFunction<String>() {
private static final long serialVersionUID = 1L;
@Override
public void call(String name) throws Exception {
System.out.println(name);
}
});
}
4.filter(func)。
官方介绍:
Return a new dataset formed by selecting those elements of the source on which func returns true.
也就是通过函数筛选出所需要的数据元素,返回true也代表保留,false代表抛弃。
操作示例:
// 过滤出一部分数据
private static void FilterOperator(JavaSparkContext sc) {
List<Integer> scores = Arrays.asList(43, 60, 59, 70, 81);
JavaRDD<Integer> scoresRDD = sc.parallelize(scores);
//筛选出分数小于60的。
JavaRDD<Integer> results = scoresRDD.filter(new Function<Integer, Boolean>() {
private static final long serialVersionUID = 1L;
@Override
public Boolean call(Integer score) throws Exception {
return score < 60;
}
});
results.foreach(new VoidFunction<Integer>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Integer score) throws Exception {
System.out.println(score);
}
});
}
5.coalesce(numPartitions)。
官方介绍:
Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset.
将RDD中的partition进行减少,尤其是在上述的filter之后使用效果更好,因为filter会可能会过滤掉大量的数据从而导致一个partition中的数据量很少,这时候使用coalesce算子可以尽量的合并partition,一定程度少减少数据倾斜的问题。
操作示例:
// 将partition的数量减少
private static void CoalesceOperator(JavaSparkContext sc) {
List<String> students = Arrays.asList("stu1", "stu2", "stu3", "stu4", "stu5", "stu6");
JavaRDD<String> cls = sc.parallelize(students, 4);// 设置为四个partition
JavaRDD<String> temp = cls.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {
private static final long serialVersionUID = 1L;
@Override
public Iterator<String> call(Integer index, Iterator<String> cls) throws Exception {
List<String> list = new ArrayList<>();
while (cls.hasNext()) {
String stu = cls.next();
stu = "1[" + index + "]" + stu;
list.add(stu);
}
return list.iterator();
}
}, true);
JavaRDD<String> temp2 = temp.coalesce(2);//将四个partition减少到两个
JavaRDD<String> result = temp2
.mapPartitionsWithIndex