RDD是spark的核心和重要组成,spark内部提供了丰富的算子供我们使用,节省了开发时间使得开发更为高效,从而让我们专注业务逻辑。因为spark丰富的算子使得它更适用于需要复杂计算的业务场景。这次将会分享一些关于RDD的概念和理论以及经典的字数统计案例
-
RDD
-
概念
RDD(Resilient Distributed Dateset),弹性分布式数据集。
-
RDD的五大特性:
-
RDD是由一系列的partition组成的。
-
函数是作用在每一个partition(split)上的。
-
RDD之间有一系列的依赖关系。
-
分区器是作用在K,V格式的RDD上。
-
RDD提供一系列最佳的计算位置。
-
RDD理解图:
-
注意:
-
textFile方法底层封装的是读取MR读取文件的方式,读取文件之前先split,默认split大小是一个block大小。
-
RDD实际上不存储数据,这里方便理解,暂时理解为存储数据。
-
什么是K,V格式的RDD?
-
如果RDD里面存储的数据都是二元组对象,那么这个RDD我们就叫做K,V格式的RDD。
-
哪里体现RDD的弹性(容错)?
-
partition数量,大小没有限制,体现了RDD的弹性。
-
RDD之间依赖关系,可以基于上一个RDD重新计算出RDD。
-
哪里体现RDD的分布式?
-
RDD是由Partition组成,partition是分布在不同节点上的。
-
RDD提供计算最佳位置,体现了数据本地化。体现了大数据中“计算移动数据不移动”的理念。
从上面我们可以知道整个spark的开发都和RDD息息相关
2 完整的字数统计案例代码
package com.debug;
import java.util.Arrays;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import scala.Tuple1;
import scala.Tuple2;
public class SparkWordCount {
public static void main(String[] args) {
SparkConf conf=new SparkConf();
conf.setMaster("local");
conf.setAppName("WordCountApp");
JavaSparkContext context=new JavaSparkContext(conf);
//读取字数统计txt文件
JavaRDD<String> lines=context.textFile("/home/cry/word.txt");
//使用flatMap遍历读取的内容-返回一个集合
JavaRDD<String> words=lines.flatMap(new FlatMapFunction<String, String>() {
public Iterable<String> call(String line) throws Exception {
return Arrays.asList(line.split(" "));
}
});
//将RDD转换成kv格式-map阶段
JavaPairRDD<String, Integer> pairWords=words.mapToPair(new PairFunction<String, String, Integer>() {
public Tuple2<String, Integer> call(String word) throws Exception {
return new Tuple2(word,1);
}
});
//reduce阶段-原理和hadoop的reduce一样
JavaPairRDD<String, Integer> result=pairWords.reduceByKey(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer num1, Integer num2) throws Exception {
return num1+num2;
}
});
//因不存在按value排序的方法,所以使用mapTopair先给kv对的rdd的key和value交换位置
JavaPairRDD<Integer, String> res3=result.mapToPair(new PairFunction<Tuple2<String,Integer>, Integer, String>() {
public Tuple2<Integer, String> call(Tuple2<String, Integer> tup) throws Exception {
return new Tuple2<Integer, String>(tup._2, tup._1);
}
});
JavaPairRDD<Integer, String> res4=res3.sortByKey(false);
res4.foreach(new VoidFunction<Tuple2<Integer,String>>() {
public void call(Tuple2<Integer, String> tup) throws Exception {
System.out.println(tup);
}
});
//按value排序后还可以再次mapTopair把key和value调换位置
//打印输出结果
/*result.foreach(new VoidFunction<Tuple2<String,Integer>>() {
public void call(Tuple2<String, Integer> tuple) throws Exception {
System.out.println(tuple);
}
});*/
context.stop();
}
}
这段代码使用的是spark1.6的API,有需要的话也可以使用2.x版本的API, 相比1.6新版本使用的是SparkSession,就不再需要使用JavaSparkContext了,可参考官网例子的代码,如下所示:
/* SimpleApp.java */
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.Dataset;
public class SimpleApp {
public static void main(String[] args) {
String logFile = "YOUR_SPARK_HOME/README.md"; // Should be some file on your system
SparkSession spark = SparkSession.builder().appName("Simple Application").getOrCreate();
Dataset<String> logData = spark.read().textFile(logFile).cache();
long numAs = logData.filter(s -> s.contains("a")).count();
long numBs = logData.filter(s -> s.contains("b")).count();
System.out.println("Lines with a: " + numAs + ", lines with b: " + numBs);
spark.stop();
}
}
新版本推荐使用DataSet,原因是它在RDD的层面做了很多优化,如果不习惯api也提供了javaRdd方法可以把结果转成rdd
下面是运行结果:
上面的代码foreach属于Action算子,其他的如flatMap、reduceByKey、sortByKey等都是转换算子,需要注意的是转换算子的代码执行需要Action算子激活(action算子被执行)