image.png
前言
本節(jié)科目主要做spark的相關入門知識
1.1、spark是什么?(what?)
spark是一個快速而通用的集群計算
平臺,擴展了MapReduce的計算模型,能夠高效的支持更多計算模式,包含交互式查詢和流處理。在spark核心數據處理引擎之上,有用于SQL,流處理,機器學習,圖形計算等的庫(如下圖)。并且spark支持很多語言,包含Java,Python,Scala等(Scala更適配哦,spark本身由scala開發(fā))。
image.png
1.2、為什么使用spark(why)?
1.2.1、haddop生態(tài)系統(tǒng)臃腫
現階段hadoop生態(tài)系統(tǒng)存在的問題
- 離線計算:MapReduce
- hive:歷史數據分析
- hbase:實時數據查詢
- storm:流處理
這就導致維護成本和學習成本很高。而spark彌補了這些。 - spark streaming:流式計算
- spark sql:即時查詢
- MLib:機器學習
- Graphx:圖處理
1.2.2、效率高
spark基于內存開發(fā)。
1.3、什么時候使用spark(when)?
- 需要多次操作特定集合:基于內存計算的原理
- 粗粒度更新狀態(tài)
1.4、怎么使用spark(how)?
我們通過一個簡單的wordCount的demo看下。
public class WordCount {
public static void main(String[] args) {
// 編寫Spark應用程序
// 本地執(zhí)行,是可以執(zhí)行在eclipse中的main方法中,執(zhí)行的
// 第一步:創(chuàng)建SparkConf對象,設置Spark應用的配置信息
// 使用setMaster()可以設置Spark應用程序要連接的Spark集群的master節(jié)點的url
// 但是如果設置為local則代表,在本地運行
SparkConf conf = new SparkConf()
.setAppName("WordCountLocal")
.setMaster("local");
// 第二步:創(chuàng)建JavaSparkContext對象
// 在Spark中,SparkContext是Spark所有功能的一個入口,你無論是用java、scala,甚至是python編寫
// 都必須要有一個SparkContext,它的主要作用,包括初始化Spark應用程序所需的一些核心組件,包括
// 調度器(DAGSchedule、TaskScheduler),還會去到Spark Master節(jié)點上進行注冊,等等
// 一句話,SparkContext,是Spark應用中,可以說是最最重要的一個對象
// 但是呢,在Spark中,編寫不同類型的Spark應用程序,使用的SparkContext是不同的,如果使用scala,
// 使用的就是原生的SparkContext對象
// 但是如果使用Java,那么就是JavaSparkContext對象
// 如果是開發(fā)Spark SQL程序,那么就是SQLContext、HiveContext
// 如果是開發(fā)Spark Streaming程序,那么就是它獨有的SparkContext
// 以此類推
JavaSparkContext sc = new JavaSparkContext(conf);
// 第三步:要針對輸入源(hdfs文件、本地文件,等等),創(chuàng)建一個初始的RDD
// 輸入源中的數據會打散,分配到RDD的每個partition中,從而形成一個初始的分布式的數據集
// 我們這里呢,因為是本地測試,所以呢,就是針對本地文件
// SparkContext中,用于根據文件類型的輸入源創(chuàng)建RDD的方法,叫做textFile()方法
// 在Java中,創(chuàng)建的普通RDD,都叫做JavaRDD
// 在這里呢,RDD中,有元素這種概念,如果是hdfs或者本地文件呢,創(chuàng)建的RDD,每一個元素就相當于
// 是文件里的一行
JavaRDD<String> lines = sc.textFile("/Users/sunliangliang/Documents/personal/csv/000002.csv");
// 第四步:對初始RDD進行transformation操作,也就是一些計算操作
// 通常操作會通過創(chuàng)建function,并配合RDD的map、flatMap等算子來執(zhí)行
// function,通常,如果比較簡單,則創(chuàng)建指定Function的匿名內部類
// 但是如果function比較復雜,則會單獨創(chuàng)建一個類,作為實現這個function接口的類
// 先將每一行拆分成單個的單詞
// FlatMapFunction,有兩個泛型參數,分別代表了輸入和輸出類型
// 我們這里呢,輸入肯定是String,因為是一行一行的文本,輸出,其實也是String,因為是每一行的文本
// 這里先簡要介紹flatMap算子的作用,其實就是,將RDD的一個元素,給拆分成一個或多個元素
JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
private static final long serialVersionUID = 1L;
@Override
public Iterator<String> call(String line) throws Exception {
String word = new String(line.getBytes("UTF-8"));
System.out.println("word = "+word);
return Arrays.asList(word.split(",")).iterator();
}
});
// 接著,需要將每一個單詞,映射為(單詞, 1)的這種格式
// 因為只有這樣,后面才能根據單詞作為key,來進行每個單詞的出現次數的累加
// mapToPair,其實就是將每個元素,映射為一個(v1,v2)這樣的Tuple2類型的元素
// 如果大家還記得scala里面講的tuple,那么沒錯,這里的tuple2就是scala類型,包含了兩個值
// mapToPair這個算子,要求的是與PairFunction配合使用,第一個泛型參數代表了輸入類型
// 第二個和第三個泛型參數,代表的輸出的Tuple2的第一個值和第二個值的類型
// JavaPairRDD的兩個泛型參數,分別代表了tuple元素的第一個值和第二個值的類型
JavaPairRDD<String, Integer> pairs = words.mapToPair(
new PairFunction<String, String, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public Tuple2<String, Integer> call(String word) throws Exception {
return new Tuple2<String, Integer>(word, 1);
}
});
// 接著,需要以單詞作為key,統(tǒng)計每個單詞出現的次數
// 這里要使用reduceByKey這個算子,對每個key對應的value,都進行reduce操作
// 比如JavaPairRDD中有幾個元素,分別為(hello, 1) (hello, 1) (hello, 1) (world, 1)
// reduce操作,相當于是把第一個值和第二個值進行計算,然后再將結果與第三個值進行計算
// 比如這里的hello,那么就相當于是,首先是1 + 1 = 2,然后再將2 + 1 = 3
// 最后返回的JavaPairRDD中的元素,也是tuple,但是第一個值就是每個key,第二個值就是key的value
// reduce之后的結果,相當于就是每個單詞出現的次數
JavaPairRDD<String, Integer> wordCounts = pairs.reduceByKey(
new Function2<Integer, Integer, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public Integer call(Integer v1, Integer v2) throws Exception {
return v1 + v2;
}
});
// 到這里為止,我們通過幾個Spark算子操作,已經統(tǒng)計出了單詞的次數
// 但是,之前我們使用的flatMap、mapToPair、reduceByKey這種操作,都叫做transformation操作
// 一個Spark應用中,光是有transformation操作,是不行的,是不會執(zhí)行的,必須要有一種叫做action
// 接著,最后,可以使用一種叫做action操作的,比如說,foreach,來觸發(fā)程序的執(zhí)行
wordCounts.foreach(new VoidFunction<Tuple2<String,Integer>>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Tuple2<String, Integer> wordCount) throws Exception {
System.out.println(wordCount._1 + " 出現 " + wordCount._2 + " 次");
}
});
sc.close();
}
}
引入的pom如下
<properties>
<spark.version>2.1.0</spark.version>
<scala.version>2.11</scala.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
1.5、其他
- 1.5.1、SparkContext:
編寫spark程序用到的第一個類,該類是spark的主要入口點,若spark集群當做服務端,那么SparkContext就是客戶端的核心;它是用于連接spark集群、創(chuàng)建RDD、累加器、廣播變量的基礎。參考官網上的圖片。
image.png
-
1.5.2、sparkcontext相關組件
image.png 1.5.3、創(chuàng)建sparkcontext
SparkConf conf = new SparkConf()
.setAppName("WordCountLocal")
.setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
注:如果不做特殊說明,后續(xù)文章中sc = JavaSparkContext
對象。
參考出處