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算子被執行)