文章目录
一、创建RDD
-
Spark Core(代替MR,进行离线批处理操作)提供了三种创建RDD的方式:
创建方式 应用场景 使用程序种的集合创建 主要用于测试,在实际部署到集群运行之前,通过集合构造测试数据集,来测试Spark应用的流程 使用本地文件创建 主要用于临时性地处理一些存储大量数据地文件 使用HDFS文件创建 最常用地生产环境处理方式,主要可以针对HDFSs上存储地大数据进行离线批处理操作 -
示例
(1) 并行化集合创建RDD => SparkContext.parallelize()
Spark将集合中地数据拷贝到集群上去,形成一个分布式的数据集合,即一个RDD。通常来讲,集合中的部分数据会归于一个节点,而另一部分数据会归于其他节点便于并行操作。
java实现/** * 并行化集合创建RDD * 案例:累加 * @author Z -Jay * */ public class ParallizeCollection { public static void main(String[] args) { //1. 创建SparkConf SparkConf conf = new SparkConf() .setAppName( "ParallizeCollection") .setMaster( "local"); //2. 创建JavvaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 若通过并行化集合的方式创建RDD,则需要调用SparkContext及其子类的parallelize() List<Integer> numbers = Arrays. asList(1,2,3,4,5,6,7,8,9,10); JavaRDD<Integer> javaRDD = sc .parallelize(numbers); //4. 定义reduce算子操作并执行,现在执行累加:1+2=3,3+3=6;6+4=10... Integer sum=javaRDD .reduce(new Function2<Integer, Integer, Integer>() { private static final long serialVersionUID = 1L; @Override public Integer call(Integer num1 , Integer num2) throws Exception { return num1 +num2 ; } }); System. out.println("1..10=" +sum ); //4. 关闭SparkContext sc.close(); } }
scala实现
class ParallelizeCollection { def main(args: Array[ String]){ val conf = new SparkConf() .setAppName( "ParallelizeCollection") .setMaster( "local") val sc = new SparkContext(conf) val numbers = Array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val numberRDD = sc.parallelize(numbers, 5) val sum = numberRDD.reduce(_+_) println(sum) } }
【注】指定切割分区数,Spark会为每一个partition运行一个task来进行处理。官方建议为集群中的每个CPU创建2-4个partition。Spark默认会根据集群的情况来设置partition的数量。
(2) 本地文件和HDFS文件创建RDD => SparkContext.textFile() ==Spark支持使用“任何Hadoop存储系统即HDFS上的文件来创建RDD”,如HDFS,CASSANDRA,HBase及本地文件。==
注意事项:
- 若只针对本地文件在Windows单机上线程模拟,则当前主机上只有一份文件即可;若针对本地文件在Spark集群上实际运行,则必须将文件拷贝到所有Worker节点上,否则报错。
- SparkContext.textFile()支持对目录、压缩文件以及通配符进行RDD创建(即多个文件)
- Spark会默认为HDFS上的文件的每一个Block创建一个partition,但也可以通过textFile()第二参数进行手动设置分区数量,只要比Block数量多即可,否则报错。
java实现-案例1
/**
* 使用本地文件创建RDD
* 案例:统计文本文件字符数
* @author Z -Jay
*
*/
public class LetterCountLocalFile {
public static void main(String[] args) {
//1. 创建SparkConf
SparkConf conf = new SparkConf()
.setAppName( "LetterCount")
.setMaster( "local");
//2. 创建JavaSparkContext
JavaSparkContext sc = new JavaSparkContext (conf );
//3. 使用SparkContext及其子类实现 textFile()创建文件初始RDD
JavaRDD<String> lines = sc.textFile("C:\\Users\\Z-Jay\\Desktop\\spark.txt" );
//4. 统计文本文件字符数
//4.1 先获取每一行的长度作为元素,获取中间RDD
JavaRDD<Integer> lineLen = lines .map(new Function<String,Integer>(){
private static final long serialVersionUID = 1L;
@Override
public Integer call(String line ) throws Exception {
return line .length();
}
});
//4.2 下一阶段对中间产生的RDD元素进行累加
Integer count =lineLen .reduce(new Function2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer num1 , Integer num2) throws Exception {
// TODO Auto-generated method stub
return num1 +num2 ;
}
});
System. out.println("Total count=" +count );
//5. 关闭SparkContext
sc.close();
}
}
scala实现-案例1
class LetterCountLocalFile {
def main(args:Array[String]){
val conf = new SparkConf(). setAppName("LetterCountHDFSFile" );
val sc = new SparkContext( conf);
val lines = sc.textFile("hdfs://spark1:9000/spark.txt", 1);
val count = lines.map{ line => line.length() }
. reduce(_+_)
println("Total count="+ count);
}
}
java实现-案例2
/**
* 使用HDFS文件创建RDD
* 案例:统计文本文件字符数
* @author Z -Jay
*
*/
public class LetterCountHDFSFile {
public static void main(String[] args) {
//1. 创建SparkConf
//*较本地文件方式的修改:去掉setMaster(),修改setAppName()
SparkConf conf = new SparkConf()
.setAppName( "HDFSFile");
//2. 创建JavaSparkContext
JavaSparkContext sc = new JavaSparkContext(conf );
//3. 使用SparkContext及其子类实现 textFile()创建文件初始RDD
//*修改文件路径为HDFS路径
JavaRDD<String> lines = sc.textFile("hdfs://spark1:9000/spark.txt" );
//4. 统计文本文件字符数
//4.1 先获取每一行的长度作为元素,获取中间RDD
JavaRDD<Integer> lineLen = lines .map(new Function<String,Integer>(){
private static final long serialVersionUID = 1L;
@Override
public Integer call(String line ) throws Exception {
return line .length();
}
});
//4.2 下一阶段对中间产生的RDD元素进行累加
Integer count =lineLen .reduce(new Function2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer num1 , Integer num2) throws Exception {
// TODO Auto-generated method stub
return num1 +num2 ;
}
});
System. out.println("Total count=" +count );
//5. 关闭SparkContext
sc.close();
}
}
scala实现-案例2
class LetterCountLocalFile {
def main(args :Array[String]){
val conf = new SparkConf().setAppName ("LetterCountHDFSFile" );
val sc = new SparkContext(conf );
val lines = sc.textFile("hdfs://spark1:9000/spark.txt", 1);
val count = lines.map{ line => line.length() }
. reduce(_+_)
println("Total count="+ count);
}
}
- 创建RDD的其他方法
方法 说明 SparkContext.wholeTextFiles() 针对一个目录中存在的大量小文件,返回结构为<fileName,fileContent>的pair,形成一个PairRDD,非普通RDD(普通的textFile()返回的RDD中,每个元素就是文件中的一行文本) SparkContext.sequenceFile[K,V]() 针对SequenceFile,泛型K,V即SequenceFile的Key与Value类型。K,V必须是Hadoop序列化类型(Text, LongWritable…) SparkContext.hadoopRDD() 针对Hadoop自定义输入类型创建RDD,该方法接受JobConf,InputFormatClass,Key和Value的class SparkContext.objectFile() 针对之前调用RDD.saveAsObject创建的序列化文件执行反序列化还原。
二、操作RDD:两类算子
-
Transformation算子:形成Task流程
项目 说明 概述 针对已有的RDD创建一个新RDD(中间结果RDD),不会返回给Driver 。 举例 算子map就是典型的Transformation。将已有的RDD中每个元素传入一个自定义函数,并获取一个新元素,之后将所有新获取的元素组成一个新RDD作为下面操作的输入。 特点 懒执行Lazy
若一个Spark应用仅定义Transformation操作,那么即使执行此应用,这些操作也不会执行。即Transformation不会触发Spark程序执行,它们与Redis事务队列化相似,只是记录了对RDD的操作流程,但不会自发的执行(没有结果的中间运算是没有意义的)。只有在定义Transformaton之后,又执行了一个Action,所有的Transformaton才会执行。 Spark正是通过这种特性,对底层的Spark应用执行优化以避免产生过多中间结果RDD。其他 特殊的Transormation,如groupByKey, sortByKey, reduceByKey等,只针对特殊RDD,即PairRDD。这种RDD中的元素,实际上是Scala的一种类型,即Tuple2。在Scala中,需要手动导入Spark相关隐士转换,import org.apache.spark.SparkContent._。然后,对应包含Tuple2的RDD就会自动转换为PairRDDFunction,并提供reduceByKey等方法。 -
Action算子:应用开关
目 说明 述 对RDD进行最后操作,如遍历/reduce/保存至文件等,且可以返回结果给Driver 。 例 算子reduce就是典型的Action,将RDD中所有元素进行聚合,获取一个最终的结果,接着返回给Driver。 点 “触发器”
一个Action操作会执行一个SparkJob,从而按流程触发该Action之前所有的Transformation执行。 -
Transformation类算子案例(本地模拟)
【常用8种:map/filter/flatMap/groupByKey/reduceByKey/sortByKey/join/cogroup】(1) map:
[1] 任何类型的RDD都可以调用map算子。
[2] 在Java中,map算子接收Function对象。创建Function对象(重写call(),调用该算子的RDD中的每个元素都会传入call()中并处理返回),必须设置第二个泛型参数R即要返回的新元素类型(第一泛型参数是调用该算子的RDD元素的类型)。
[3] 所有返回的新元素会组成一个新的RDD。
[4] java实现-案例/** * map算子 * 案例:将集合中每一个元素都乘2 */ private static void mapOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("map" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext (conf ); //3. 构建集合 List<Integer> numbers = Arrays.asList(1,2,3,4,5); //4. 并行化集合创建初始RDD JavaRDD<Integer> javaRDD = sc .parallelize(numbers); //5. 使用map算子,将初始RDD中的每个元素都乘2 JavaRDD<Integer> resRDD = javaRDD .map(new Function<Integer, Integer>() { private static final long serialVersionUID = 1L; @Override public Integer call(Integer v ) throws Exception { return v *2; } }); //6. 使用foreach算子,将结果RDD的每个元素进行打印 resRDD.foreach(new VoidFunction<Integer>() { private static final long serialVersionUID = 1L; @Override public void call(Integer num) throws Exception { System. out.println(num ); } }); //7. 关闭SparkContext sc.close(); }
[5] scala实现-案例
def mapOp(){ val conf = new SparkConf().setAppName ("mapOp" ).setMaster ("local" ) val sc = new SparkContext(conf ) val numbers = Array(1,2,3,4,5) val srcRDD = sc.parallelize(numbers, 5) val resRDD = srcRDD.map(num => num* 2) resRDD.foreach( num => println(num) ) sc.close(); } }
(2) filter:
[1] filter同样接收Function对象,只是泛型参数不同,固定为Boolean。若想在新RDD中保留该元素,则返回true,否则返回false。
[2] java实现-案例/** * filter算子 * 案例:过滤集合中的偶数 */ private static void filterOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("filter" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 并行化集合创建初始RDD JavaRDD< Integer> javaRDD = sc.parallelize(Arrays.asList(1,2,3,4,5,6,7,8,9,10)); //4. 使用filter算子,过滤出其中偶数 JavaRDD< Integer> resRDD = javaRDD.filter(new Function<Integer, Boolean>() { private static final long serialVersionUID = 1L; @Override public Boolean call(Integer num) throws Exception { Boolean flag=false ; if(num %2==0) flag=true ; return flag ; } }); resRDD.foreach(new VoidFunction<Integer >() { private static final long serialVersionUID = 1L; @Override public void call(Integer even ) throws Exception { System. out.println(even ); } }); // sc.close(); }
[3] scala实现-案例
def filterOp(){ val conf = new SparkConf().setAppName ("filter" ).setMaster ("local" ) val sc = new SparkContext(conf ) val numbers = Array(1,2,3,4,5) val srcRDD = sc.parallelize(numbers, 5) val resRDD = srcRDD.filter(number => number % 2== 0) resRDD.foreach( even => println(even) ) sc.close() }
(3) flatMap
[1] flatMap算子接收FlatMapFunction参数。其第二泛型参数U即为返回的新元素类型,call()返回的不是U,而是Iterable<U>,即返回一个包含多个元素的可迭代容器(List, Set …)。
[2] java实现-案例/** * flatMap算子 * 案例:将文本行拆分为多个单词 */ private static void flatMapOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("flatMap" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 并行化集合,创建初始RDD JavaRDD<String> javaRDD = sc.textFile("C:\\Users\\Z-Jay\\Desktop\\spark.txt" ); //4. 使用flatMap算子,将每一行文本拆分为“多个”单词 //新的RDD中,每一个元素都是一个包含该行单词的容器迭代器 JavaRDD<String> resRDD = javaRDD .flatMap(new FlatMapFunction<String, String>() { private static final long serialVersionUID = 1L; @Override public Iterator<String> call(String line ) throws Exception { return Arrays.asList( line.split(" ")).iterator(); } }); //5. 打印结果RDD resRDD.foreach(new VoidFunction<String>() { private static final long serialVersionUID = 1L; @Override public void call(String word) throws Exception { System. out.println(word ); } }); //6.关闭SparkContext sc.close(); }
[3] scala实现-案例
def flatMapOp(){ val conf = new SparkConf().setAppName ("flatMap" ).setMaster ("local" ) val sc = new SparkContext(conf ) val srcRDD = sc.textFile("C:\\ Users\\Z-Jay \\Desktop\\ spark.txt") val resRDD = srcRDD.flatMap(line => line.split( " ")) resRDD.foreach( word => println(word) ) sc.close() }
(4) groupByKey
[1] 只针对特殊键值对RDD——JavaPairRDD的算子,其中的元素类型为二元组Tuple。返回类型仍然是JavaPairRDD,且Key的类型不变,value类型变为之前元素的可迭代容器类型Iterable。
[2] java实现-案例/** * groupByKey 特殊Transformation算子处理Tuple键值对 * 案例:按班级对成绩分组 */ private static void groupByKeyOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("groupByKeyOp" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 构建模拟集合,元素类型为Tuple2 二元组 List<Tuple2<String,Integer>> scores = Arrays.asList( new Tuple2<String,Integer>("class01" ,80), new Tuple2<String,Integer>("class02" ,30), new Tuple2<String,Integer>("class01" ,10), new Tuple2<String,Integer>("class01" ,100), new Tuple2<String,Integer>("class02" ,70) ); //4. 并行化集合,创建初始RDD,注意类型为JavaPairRDD,与Tuple2对应 JavaPairRDD<String,Integer> javaPairRDD = sc.parallelizePairs(scores ); //5. 对pairRDD的每个Tuple2元素,执行groupByKey算子,按照班级进行分组 JavaPairRDD<String, Iterable<Integer>> groupedRDD = javaPairRDD.groupByKey(); //6. 打印分组后的RDD groupedRDD.foreach(new VoidFunction<Tuple2<String,Iterable<Integer>>>() { private static final long serialVersionUID = 1L; //遍历每一个Tuple2类型的元素,第一元素为班名字符串,第二元素为成绩的可迭代容器 @Override public void call(Tuple2<String, Iterable<Integer>> tuple) throws Exception { System. out.println("class:" +tuple ._1 ); Iterator<Integer> iter = tuple ._2 .iterator(); while(iter .hasNext()){ System. out.println(iter .next()); } System. out.println("===========================" ); } }); //7. 关闭SparkContext sc.close(); }
[3] scala实现-案例
def groupByKeyOp(){ val conf = new SparkConf().setAppName ("groupByKey" ).setMaster ("local" ) val sc = new SparkContext(conf ) val scoreList = Array( Tuple2("class01" ,80 ) , Tuple2("class02", 30) , Tuple2("class01" ,10 ) , Tuple2("class01", 100) , Tuple2("class02" ,70 ) ) val scores = sc.parallelize(scoreList, 5) val groupedSocres = scores. groupByKey() groupedSocres. foreach(tuple => ( println(tuple_1); tuple._2.foreach (score => println(score) ); println( "========================================" ) )) }
(5) reduceByKey
[1] reduceByKey算子接收Function2对象,第一个和第二个泛型类型即为当前RDD中二元组的value类型,即调用一次call(),将传入两个连续的value值进行“聚合”。第三个泛型类型即为每一次返回结果的类型,默认与当前RDD的value类型相同。
[2] reduceByKey算子同样返回JavaPairRDD,Tuple2<>(key,聚合结果)
[3] java实现-案例/** * reduceByKey算子 * 案例:统计每个班的总分 */ private static void reduceByKeyOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("reduceByKey" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 构建模拟集合,元素类型为Tuple2 二元组 List<Tuple2<String,Integer>> scores = Arrays.asList( new Tuple2<String,Integer>("class01" ,80), new Tuple2<String,Integer>("class02" ,30), new Tuple2<String,Integer>("class01" ,10), new Tuple2<String,Integer>("class01" ,100), new Tuple2<String,Integer>("class02" ,70) ); //4. 并行化集合,创建初始RDD,注意类型为JavaPairRDD,与Tuple2对应 JavaPairRDD<String,Integer> pairRDD = sc.parallelizePairs(scores ); //5. 对pairRDD的每个Tuple2元素,执行reduceByKey算子,按照班级进行累加 JavaPairRDD<String,Integer> pairRDD2 = pairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() { private static final long serialVersionUID = 1L; @Override public Integer call(Integer val1 , Integer val2) throws Exception { return val1 +val2 ; } }); //6. 打印结果RDD pairRDD2.foreach(new VoidFunction<Tuple2<String,Integer>>() { private static final long serialVersionUID = 1L; @Override public void call(Tuple2<String, Integer> tuple) throws Exception { System. out.println("class " +tuple ._1 +"'s total:"+tuple._2); } }); //7. 关闭SparkContext sc.close(); }
[4] scala实现-案例
def reduceByKeyOp(){ val conf = new SparkConf().setAppName ("reduceByKey" ).setMaster ("local" ) val sc = new SparkContext(conf ) val scoreList = Array( Tuple2("class01" ,80 ) , Tuple2("class02", 30) , Tuple2("class01" ,10 ) , Tuple2("class01", 100) , Tuple2("class02" ,70 ) val scores = sc.parallelize(scoreList, 5) val groupedSocres = scores. reduceByKey(_+_) groupedSocres. foreach(tuple => println( "class "+tuple_1)+"'s score:" +tuple._2) }
(6) sortByKey
[1] java实现-案例/** * sortByKey算子 * 案例:按照学生分数进行排序 */ private static void sortByKey(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("sortByKey" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 构建模拟集合,元素类型为Tuple2 二元组 List<Tuple2<Integer,String>> scores = Arrays.asList( new Tuple2<Integer,String>(80,"tom-z" ), new Tuple2<Integer,String>(30,"tom-x" ), new Tuple2<Integer,String>(80,"tom" ), new Tuple2<Integer,String>(60,"amy" ), new Tuple2<Integer,String>(100,"fiona" ) ); //4. 并行化集合,创建初始RDD,注意类型为JavaPairRDD,与Tuple2对应 JavaPairRDD<Integer,String> pairRDD = sc.parallelizePairs(scores ); //5. 对pairRDD执行sortByKey算子,按照成绩进行排序,默认升序,指定为false则降序 JavaPairRDD<Integer,String> pairRDD2 = pairRDD.sortByKey(false); //6. 打印结果RDD pairRDD2.foreach(new VoidFunction<Tuple2<Integer,String>>() { private static final long serialVersionUID = 1L; @Override public void call(Tuple2<Integer, String> tuple) throws Exception { System. out.println("score "+tuple._1+":"+ tuple._2 ); } }); //7. 关闭SparkContext sc.close(); }
[2] scala实现-案例
def sortByKey(){ val conf = new SparkConf ().setAppName ("sortByKey" ).setMaster ("local" ) val sc = new SparkContext(conf ) val scoreList = Array( Tuple2(80 ,"tom-z" ) , Tuple2(30,"tom-x") , Tuple2(80 ,"tom" ) , Tuple2(60,"amy") , Tuple2(100 ,"fiona" ) ) val scores = sc.parallelize(scoreList, 5) val sortedScores = scores. sortByKey() sortedSocres.foreach (tuple => println(tuple._1+":" +tuple._2) }
(7) 高级算子join
[1] join算子由一个JavaPairRDD调用,接收一个JavaPairRDD为参数。根据key实现SQL中的join功能,返回连接后的结果,返回的类型为JavaPairRDD,其元素形式为Tuple2<共用键,Tuple2<调用RDD中的值,参数RDD中的值>>。
[2] java实现-案例/** * join算子 * 案例:打印学生成绩 */ private static void joinOp(){ //1. 创建SparkConf SparkConf conf = new SparkConf().setAppName("join" ).setMaster("local"); //2. 创建JavaSparkContext JavaSparkContext sc = new JavaSparkContext(conf ); //3. 构建模拟集合 List<Tuple2<Integer,String>> stuList = Arrays.asList( new Tuple2<Integer,String>(1,"tom-z" ), new Tuple2<Integer,String>(2,"tom-x" ), new Tuple2<Integer,String>(3,"tom" ), new Tuple2<Integer,String>(4,"amy" ), new Tuple2<Integer,String>(5,"fiona" ) ); List<Tuple2<Integer,Integer>> scoreList = Arrays.asList( new Tuple2<Integer,Integer>(1,80), new Tuple2<Integer,Integer>(2,30), new Tuple2<Integer,Integer>(3,70), new Tuple2<Integer,Integer>(4,60), new Tuple2<Integer,Integer>(5,100) ); //4. 并行化集合,创建两个RDD JavaPairRDD<Integer,String> students = sc.parallelizePairs(stuList ); JavaPairRDD<Integer, Integer> scores = sc.parallelizePairs(scoreList ); //5. 对两个pairRDD执行join算子,执行类似SQL JOIN的操作 JavaPairRDD<Integer, Tuple2<String, Integer>> resRDD = students.join(scores ); //6.打印结果RDD resRDD.foreach(new VoidFunction<Tuple2<Integer,Tuple2<String,Integer>>>() { private static final long serialVersionUID = 1L; @Override public void call(Tuple2<Integer, Tuple2<String, Integer>> tuple) throws Exception { System.out.println( "student id: "+tuple ._1 ); System.out.println( "student name:"+tuple ._2 ._1 ); System.out.println( "student score:"+tuple._2._2); } }); //7. 关闭SparkContext sc.close(); }
[3] scala实现-案例
def joinOp(){ val conf = new SparkConf().setAppName ("join" ).setMaster ("local" ) val sc = new SparkContext(conf ) val stuList = Array( Tuple2( 1, "tom-z") , Tuple2 (2 ,"tom-x" ) , Tuple2(3,"tom") , Tuple2(4 ,"amy" ) , Tuple2(5,"fiona") ) val scoreList = Array( Tuple2(1 ,80 ) , Tuple2(2,70) , Tuple2( 3, 60) , Tuple2(4 ,50 ) , Tuple2(5,100) ) val stus = sc.parallelize(stuList, 5) val scores = sc.parallelize(scoreList, 5) val resRDD = stus.join(scores) resRDD.foreach(tuple => ( println( "student id"+tuple._1); println( "student name "+tuple._2._1) println( "student score "+tuple._2._2) )) }