RDD转换方法回顾
Rdd转换算子就是将旧的RDD通过方法转换成一个新的RDD
之所以这样做,就是希望将多个RDD的功能组合在一起(装饰着设计模式)
RDD行动算子介绍
行动算子和转换算子如何区分?
转换算子:将旧的RDD转换成新的RDD,为了组合多个RDD功能
看返回值是否为新的RDD,是则为转换算子。返回值是一个具体的值,则是行动算子
不能看是否触发作业执行来判断
RDD行动算子-Collect
collect方法就是RDD行动算子
RDD的行动算子会触发作业job的执行
collect方法就是将Exector端执行的结果按照分区的顺序拉取(采集)回到Driver端,将结果组合成集合对象
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
JavaRDD<Object> newRdd = rdd.map(num->num*2);
newRdd.collect().forEach(System.out::println);
如图:如图:要注意不同executor返回的对象顺序不一定 但是内部有顺序
Spark的计算全部都是在Executor端执行
Spark在编写代码时,调用转换算子,并不会真正的执行,因为只是在Driver端组合功能
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
//Spark的计算全部都是在Executor端执行的
//Spark在编写代码时,调用转换算子,并不会真正的执行,因为只是在Driver端组合功能
//所以当前代码其实是在Driver端执行
//所以当前的main方法也是driver方法,当前运行main线程,也称之Driver线程
//转换算子中的逻辑代码是在Executor端执行的,并不会在Driver端调用执行
JavaRDD<Object> newRdd = rdd.map(num-> {
System.out.println("******");
return num*2;
});
//TODO collect方法就是RDD行动算子
// RDD的行动算子会触发作业job的执行
// collect方法就是将Exector端执行的结果按照分区的顺序拉取(采集)回到Driver端,将结果组合成集合对象
newRdd.collect().forEach(System.out::println);
如果不是数字,是HDFS中的文件怎么办?如图会先分片,再去hdfs中拉取文件
所以collect方法可能会导致多个Executor的大量数据拉取到Driver端,导致内存溢出,所以生产环境慎用
RDD行动算子-其他方法
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
//TODO 用于采集数据
List<Integer> collect = rdd.collect();
//TODO count获取结果数量
long count = rdd.count();
//TODO first获取结果第一个
Integer first = rdd.first();
//TODO take从结果中获取前N个
List<Integer> take = rdd.take(3);
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
JavaPairRDD<String, Integer> pairRDD = rdd.mapToPair(
num -> new Tuple2<>("a", num)
);
//TODO 保存
pairRDD.saveAsTextFile("output1");
pairRDD.saveAsObjectFile("output2");
//单点循环
rdd.collect().forEach(System.out::println);
//分布式循环
//TODO foreach执行效率低 但是占内存比较小
rdd.foreach(
num-> System.out.println(num)
);
//TODO foreachPartition执行效率高,但是依托于内存大小
rdd.foreachPartition(
list->{
System.out.println(list);
}
);
说明一下Driver端和Excutor端的数据传输的例子
本例子用对象中的数据和被传输到Excutor的数据进行想加,开始时只是普通的对象,没加implements Serializable会报错
之后在Student对象后面加上implements Serializable,要将Driver端的对象通过网络传递到Executor端,所以这里传输的对象必须要实现可序列化接口,否则无法传递
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
JavaPairRDD<String, Integer> pairRDD = rdd.mapToPair(
num -> new Tuple2<>("a", num)
);
//TODO 对象是在driver执行的
Student s = new Student();
//foreach算子是分布式循环:分区内有序,分区间无序
rdd.foreach(
num->{
//TODO 在Executor端循环遍历的时候使用到Driver端对象
// 运行过程中,就需要将Driver端的对象通过网络传递到Executor端,否则无法使用
// 这里传输的对象必须要实现可序列化接口,否则无法传递
System.out.println(s.age+num);
}
);
jsc.close();
}
}
class Student implements Serializable {
public int age=30;
}
RDD-序列化问题1
当在Executor端用到在Driver的数据,需要拉取数据进行序列化,当发现用到的数据没有序列化时会报错。
以下代码为正确代码:除了RDD方法在Executor端执行,其他都在Driver端,但是rdd.filter用到的q和Search对象没关系,并且String方法可以序列化,所以不会有问题。
public class Spark19_Operate_Action {
public static void main(String[] args) {
//TODO 构建spark配置对象
final SparkConf conf = new SparkConf();
conf.setMaster("local");
conf.setAppName("spark");
//TODO 构建spark的运行环境
JavaSparkContext jsc = new JavaSparkContext(conf);
List<String> nums = Arrays.asList("Hadoop", "Hive", "Spark", "Flink");
JavaRDD<String> rdd = jsc.parallelize(nums,2);
//RDD算子的逻辑代码实在Excutor端执行的,其他的都是Driver端
Search search=new Search("H");
search.match(rdd);
jsc.close();
}
}
class Search{
private String query;
public Search(String query){
this.query=query;
}
public void match(JavaRDD<String> rdd){
String q=this.query;
rdd.filter(
s->s.startsWith(q)
).collect().forEach(System.out::println);
}
}
如果Search对象为如下这样:就需要对Search对象进行序列化,不然就会报错误。
class Search implements Serializable{
private String query;
public Search(String query){
this.query=query;
}
public void match(JavaRDD<String> rdd){
rdd.filter(
s->s.startsWith(query)
).collect().forEach(System.out::println);
}
}
RDD-序列化问题2
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3,4),2);
//foreach算子是分布式循环:分区内有序,分区间无序
rdd.foreach(
num->System.out.println(num)
);
//Jdk1.8的函数式编程其实采用的是对象模拟出来的
rdd.foreach(System.out::println);//会报错,出现没有序列化问题,所以不能用函数式编程