spark內存計算框架
1. spark是什麼
-
Apache Spark™ is a unified analytics engine for large-scale data processing.
-
spark是針對於``大規模數據處理`的統一分析引擎
-
spark是在Hadoop基礎上的改進,是UC Berkeley AMP lab所開源的
類Hadoop MapReduce
的通用的並行計算框架,Spark基於map reduce
算法實現的分佈式計算,擁有Hadoop MapReduce所具有的優點; -
但不同於MapReduce的是
Job
中間輸出和結果可以保存在內存
中,從而不再需要讀寫HDFS
,因此Spark能更好地適用於數據挖掘與機器學習等需要迭代的map reduce的算法 -
spark是基於
內存計算
框架,計算速度非常之快,但是它僅僅只是涉及到計算,並沒有涉及到數據的存儲,後期需要使用spark
對接外部的數據源,比如hdfs
2. spark的四大特性
2.1 速度快
-
運行速度提高
100
倍 -
Apache Spark使用最先進的
DAG
調度程序,查詢優化程序和物理執行引擎,實現批量和流式數據
的高性能 -
spark比mapreduce快的2個主要原因 :
-
1、基於內存
- mapreduce任務後期再計算的時候,每一個job的輸出結果會落地到磁盤,後續有其他的job需要依賴於前面job的輸出結果,這個時候就需要進行大量的
磁盤io
操作。性能就比較低 - spark任務後期再計算的時候,job的輸出結果可以保存在
內存
中,後續有其他的job需要依賴於前面job的輸出結果,這個時候就直接從內存中獲取得到,避免了磁盤io操作,性能比較高 - spark程序在
shuffle階段
產生的數據都會落地到磁盤中
- mapreduce任務後期再計算的時候,每一個job的輸出結果會落地到磁盤,後續有其他的job需要依賴於前面job的輸出結果,這個時候就需要進行大量的
-
2、進程與線程
-
mapreduce任務以
進程
的方式運行在yarn集羣中,比如程序中有100個MapTask,一個task就需要一個進程,這些task要運行就需要開啓100個進程。 -
spark任務以
線程
的方式運行在進程中,比如程序中有100個MapTask,後期一個task就對應一個線程,這裏就不在是進程,這些task需要運行,這裏可以極端一點:只需要開啓1個進程,在這個進程中啓動100個線程就可以了。進程中可以啓動很多個線程,而開啓一個進程與開啓一個線程需要的時間和調度代價是不一樣。 開啓一個進程需要的時間遠遠大於開啓一個線程。
-
-
2.2 易用性
- 可以快速去編寫spark程序通過
java/scala/python/R/SQL
等不同語言
2.3 通用性
- spark框架不在是一個簡單的框架,可以把spark理解成一個生態系統,它內部是包含了很多模塊,基於不同的應用場景可以選擇對應的模塊去使用
- sparksql :通過sql去開發spark程序做一些
離線分析
- sparkStreaming : 主要是用來解決公司有
實時計算
的這種場景 - Mlib :它封裝了一些
機器學習
的算法庫 - Graphx :圖計算
- sparksql :通過sql去開發spark程序做一些
2.4 兼容性
- spark程序就是一個計算邏輯程序,這個任務要運行就需要計算資源(
內存、cpu、磁盤
),哪裏可以給當前這個任務提供計算資源,就可以把spark程序提交到哪裏去運行- standAlone
- 它是spark自帶的獨立運行模式,整個任務的資源分配由spark集羣的老大Master負責
- yarn
- 可以把spark程序提交到yarn中運行,整個任務的資源分配由yarn中的老大ResourceManager負責
- mesos
- 它也是apache開源的一個類似於yarn的資源調度平臺
- standAlone
3. spark集羣架構
-
Driver
- 它會執行客戶端寫好的main方法,它會構建一個名叫SparkContext對象
- 該對象是所有spark程序的執行入口
-
Application
- 就是一個spark的應用程序,它是包含了客戶端的代碼和任務運行的資源信息
-
ClusterManager
- 它是給程序提供計算資源的外部服務
- standAlone : 它是spark自帶的集羣模式,整個任務的資源分配由spark集羣的老大Master負責
- yarn : 可以把spark程序提交到yarn中運行,整個任務的資源分配由yarn中的老大ResourceManager負責
- mesos : 它也是apache開源的一個類似於yarn的資源調度平臺
- 它是給程序提供計算資源的外部服務
-
Master
- 它是整個spark集羣的主節點,負責任務資源的分配
-
Worker
- 它是整個spark集羣的從節點,負責任務計算的節點
-
Executor
- 它是一個進程,它會在worker節點啓動該進程(計算資源)
-
Task
- spark任務是以task線程的方式運行在worker節點對應的executor進程中
4. spark集羣安裝部署
- 事先搭建好zookeeper集羣
-
1、下載安裝包
-
2、規劃安裝目錄
- /zsc/install
-
3、上傳安裝包到服務器
-
4、解壓安裝包到指定的安裝目錄
tar -zxvf spark-2.3.3-bin-hadoop2.7.tgz -C /zsc/install
-
5、重命名解壓目錄
mv spark-2.3.3-bin-hadoop2.7 spark
-
6、修改配置文件
-
進入到spark的安裝目錄下對應的conf文件夾
-
vim spark-env.sh (
mv spark-env.sh.template spark-env.sh
)#配置java的環境變量 export JAVA_HOME=/zsc/install/jdk1.8.0_141 #配置zk相關信息 export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=node01:2181,node02:2181,node03:2181 -Dspark.deploy.zookeeper.dir=/spark"
-
vim slaves (
mv slaves.template slaves
)#指定spark集羣的worker節點 node02 node03
-
-
-
7、分發安裝目錄到其他機器
scp -r /zsc/install/spark node02:/zsc/install scp -r /zsc/install/spark node03:/zsc/install
-
8、修改spark環境變量
-
vim /etc/profile
export SPARK_HOME=/zsc/install/spark export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin
-
-
9、分發spark環境變量到其他機器
scp /etc/profile node02:/etc scp /etc/profile node03:/etc
-
10、讓所有機器的spark環境變量生效
- 在所有節點執行
source /etc/profile
- 在所有節點執行
5. spark集羣的啓動和停止
5.1 啓動
- 1、先啓動zk
- 2、啓動spark集羣
- 可以在任意一臺服務器來執行(條件:需要任意2臺機器之間實現ssh免密登錄)
- $SPARK_HOME/sbin/start-all.sh
- 在哪裏啓動這個腳本,就在當前該機器啓動一個Master進程
- 整個集羣的worker進程的啓動由slaves文件決定
- 後期可以在其他機器單獨在啓動master
- $SPARK_HOME/sbin/start-master.sh
- 可以在任意一臺服務器來執行(條件:需要任意2臺機器之間實現ssh免密登錄)
-
如何恢復到上一次活着master掛掉之前的狀態?
- 在高可用模式下,整個spark集羣就有很多個master,其中只有一個master被zk選舉成活着的master,其他的多個master都處於standby,同時把整個spark集羣的元數據信息通過zk中節點進行保存
- 後期如果活着的master掛掉。首先zk會感知到活着的master掛掉,下面開始在多個處於standby中的master進行選舉,再次產生一個活着的master,這個活着的master會讀取保存在zk節點中的spark集羣元數據信息,恢復到上一次master的狀態。整個過程在恢復的時候經歷過了很多個不同的階段,每個階段都需要一定時間,最終恢復到上個活着的master的轉態,整個恢復過程一般需要
1-2
分鐘
-
在master的恢復階段對任務的影響?
-
對
已經運行的任務
是沒有任何影響 : 由於該任務正在運行,說明它已經拿到了計算資源,這個時候就不需要master -
對即
將要提交的任務
是有影響 : 由於該任務需要有計算資源,這個時候會找活着的master去申請計算資源,由於沒有一個活着的master,該任務是獲取不到計算資源,也就是任務無法運行
-
5.2 停止
-
在處於active Master主節點執行
- $SPARK_HOME/sbin/stop-all.sh
-
在處於standBy Master主節點執行
- $SPARK_HOME/sbin/stop-master.sh
6. spark集羣的web UI
- 當啓動好spark集羣之後,可以訪問 : http://node01:8080
- 我們可以通過web界面觀察到很多信息
- 整個spark集羣的詳細信息
- 整個spark集羣總的資源信息
- 整個spark集羣已經使用的資源信息
- 整個spark集羣還剩的資源信息
- 整個spark集羣正在運行的任務信息
- 整個spark集羣已經完成的任務信息
- 等等
7. 初識spark程序
7.1 普通模式提交 (指定活着的master地址)
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://node01:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
examples/jars/spark-examples_2.11-2.3.3.jar \
10
####參數說明
--class:指定包含main方法的主類
--master:指定spark集羣master地址
--executor-memory:指定任務在運行的時候需要的每一個executor內存大小
--total-executor-cores: 指定任務在運行的時候需要總的cpu核數
7.2 高可用模式提交 (集羣有很多個master)
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://node01:7077,node02:7077,node03:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
examples/jars/spark-examples_2.11-2.3.3.jar \
10
# spark集羣中有很多個master,並不知道哪一個master是活着的master,即使你知道哪一個master是活着的master,它也有可能下一秒就掛掉,這裏就可以把所有master都羅列出來:
# --master spark://node01:7077,node02:7077,node03:7077
# 後期程序會輪訓整個master列表,最終找到活着的master,然後向它申請計算資源,最後運行程序
8. spark-shell使用
8.1 運行spark-shell --master local[N] 讀取本地文件進行單詞統計
-
–master local[N]
- local 表示程序在本地進行計算,跟spark集羣目前沒有任何關係
- N 它是一個正整數,表示使用N個線程參與任務計算
- local[N] 表示本地採用N個線程計算任務
-
spark-shell --master local[2] : 默認會產生一個SparkSubmit進程
sc.textFile("file:///home/hadoop/words.txt").flatMap(x=>x.split(" ")).map(x=>(x,1)).reduceByKey((x,y)=>x+y).collect sc.textFile("file:///home/hadoop/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
8.2 運行spark-shell --master local[N] 讀取HDFS上文件進行單詞統計
-
spark整合HDFS
- 在node01上修改配置文件
- vim spark-env.sh
export HADOOP_CONF_DIR=/zsc/install/hadoop-2.6.0-cdh5.14.2/etc/hadoop
- 分發到其他節點
scp spark-env.sh node02:/zsc/install/spark/conf scp spark-env.sh node03:/zsc/install/spark/conf
-
spark-shell --master local[2]
sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect sc.textFile("hdfs://node01:8020/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
8.3 運行spark-shell 指定集羣中活着master 讀取HDFS上文件進行單詞統計
-
spark-shell --master spark://node01:7077 --executor-memory 1g --total-executor-cores 4
- –master spark://node01:7077 : 指定活着的master地址
- –executor-memory 1g : 指定每一個executor進程的內存大小
- –total-executor-cores 4 : 指定總的executor進程cpu核數
sc.textFile("hdfs://node01:8020/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect //實現讀取hdfs上文件之後,需要把計算的結果保存到hdfs上 sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).saveAsTextFile("/out")
9. 使用IDEA開發spark程序
-
構建maven工程,添加pom依賴
<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.11</artifactId> <version>2.3.3</version> </dependency> </dependencies> <build> <sourceDirectory>src/main/scala</sourceDirectory> <testSourceDirectory>src/test/scala</testSourceDirectory> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.2.2</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> <configuration> <args> <arg>-dependencyfile</arg> <arg>${project.build.directory}/.scala_dependencies</arg> </args> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass></mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
-
創建 src/main/scala 和 src/test/scala 目錄
9.1 利用scala語言開發spark程序實現單詞統計–本地運行
package com.zsc
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
//todo: 利用scala語言開發spark程序實現單詞統計
object WordCount {
def main(args: Array[String]): Unit = {
//1、構建sparkConf對象 設置application名稱和master地址
val sparkConf: SparkConf = new SparkConf().setAppName("WordCount").setMaster("local[2]")
//2、構建sparkContext對象,該對象非常重要,它是所有spark程序的執行入口
// 它內部會構建 DAGScheduler和 TaskScheduler 對象
val sc = new SparkContext(sparkConf)
//設置日誌輸出級別
sc.setLogLevel("warn")
//3、讀取數據文件
val data: RDD[String] = sc.textFile("D:\\words.txt")
//4、 切分每一行,獲取所有單詞
val words: RDD[String] = data.flatMap(x=>x.split(" "))
//5、每個單詞計爲1
val wordAndOne: RDD[(String, Int)] = words.map(x => (x,1))
//6、相同單詞出現的1累加
val result: RDD[(String, Int)] = wordAndOne.reduceByKey((x,y)=>x+y)
//按照單詞出現的次數降序排列 第二個參數默認是true表示升序,設置爲false表示降序
val sortedRDD: RDD[(String, Int)] = result.sortBy( x=> x._2,false)
//7、收集數據打印
val finalResult: Array[(String, Int)] = sortedRDD.collect()
finalResult.foreach(println)
//8、關閉sc
sc.stop()
}
}
9.2 利用scala語言開發spark程序實現單詞統計–集羣運行
package com.zsc;
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
//todo: 利用scala語言開發spark程序實現單詞統計
object WordCountOnSpark {
def main(args: Array[String]): Unit = {
//1、構建sparkConf對象 設置application名稱
val sparkConf: SparkConf = new SparkConf().setAppName("WordCountOnSpark")
//2、構建sparkContext對象,該對象非常重要,它是所有spark程序的執行入口
// 它內部會構建 DAGScheduler和 TaskScheduler 對象
val sc = new SparkContext(sparkConf)
//設置日誌輸出級別
sc.setLogLevel("warn")
//3、讀取數據文件
val data: RDD[String] = sc.textFile(args(0))
//4、 切分每一行,獲取所有單詞
val words: RDD[String] = data.flatMap(x=>x.split(" "))
//5、每個單詞計爲1
val wordAndOne: RDD[(String, Int)] = words.map(x => (x,1))
//6、相同單詞出現的1累加
val result: RDD[(String, Int)] = wordAndOne.reduceByKey((x,y)=>x+y)
//7、把計算結果保存在hdfs上
result.saveAsTextFile(args(1))
//8、關閉sc
sc.stop()
}
}
- 打成jar包提交到集羣中運行
spark-submit \
--master spark://node01:7077,node02:7077 \
--class com.kaikeba.WordCountOnSpark \
--executor-memory 1g \
--total-executor-cores 4 \
original-spark_class01-1.0-SNAPSHOT.jar \
/words.txt /out
9.3 利用java語言開發spark程序實現單詞統計–本地運行
package com.zsc
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 scala.Tuple2;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
//todo: 利用java語言開發spark的單詞統計程序
public class JavaWordCount {
public static void main(String[] args) {
//1、創建SparkConf對象
SparkConf sparkConf = new SparkConf().setAppName("JavaWordCount").setMaster("local[2]");
//2、構建JavaSparkContext對象
JavaSparkContext jsc = new JavaSparkContext(sparkConf);
//3、讀取數據文件
JavaRDD<String> data = jsc.textFile("D:\\words.txt");
//4、切分每一行獲取所有的單詞 scala: data.flatMap(x=>x.split(" "))
JavaRDD<String> wordsJavaRDD = data.flatMap(new FlatMapFunction<String, String>() {
public Iterator<String> call(String line) throws Exception {
String[] words = line.split(" ");
return Arrays.asList(words).iterator();
}
});
//5、每個單詞計爲1 scala: wordsJavaRDD.map(x=>(x,1))
JavaPairRDD<String, Integer> wordAndOne = wordsJavaRDD.mapToPair(new PairFunction<String, String, Integer>() {
public Tuple2<String, Integer> call(String word) throws Exception {
return new Tuple2<String, Integer>(word, 1);
}
});
//6、相同單詞出現的1累加 scala: wordAndOne.reduceByKey((x,y)=>x+y)
JavaPairRDD<String, Integer> result = wordAndOne.reduceByKey(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer v1, Integer v2) throws Exception {
return v1 + v2;
}
});
//按照單詞出現的次數降序 (單詞,次數) -->(次數,單詞).sortByKey----> (單詞,次數)
JavaPairRDD<Integer, String> reverseJavaRDD = result.mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {
public Tuple2<Integer, String> call(Tuple2<String, Integer> t) throws Exception {
return new Tuple2<Integer, String>(t._2, t._1);
}
});
JavaPairRDD<String, Integer> sortedRDD = reverseJavaRDD.sortByKey(false).mapToPair(new PairFunction<Tuple2<Integer, String>, String, Integer>() {
public Tuple2<String, Integer> call(Tuple2<Integer, String> t) throws Exception {
return new Tuple2<String, Integer>(t._2, t._1);
}
});
//7、收集打印
List<Tuple2<String, Integer>> finalResult = sortedRDD.collect();
for (Tuple2<String, Integer> t : finalResult) {
System.out.println("單詞:"+t._1 +"\t次數:"+t._2);
}
jsc.stop();
}
}