什麼是Spark
基於內存的,用於大規模數據處理(離線計算、實時計算、快速查詢(交互式查詢))的統一分析引擎。
Spark特點
快:
Spark計算速度是MapReduce計算速度的10-100倍
易用:(算法多)
MR支持1種計算模型,Spsark支持更多的計算模型。
通用:
Spark 能夠進行離線計算、交互式查詢(快速查詢)、實時計算、機器學習、圖計算等
兼容性:
Spark支持大數據中的Yarn調度,支持mesos。可以處理hadoop計算的數據。
Spark發展史
1、2009年誕生於美國加州大學伯克利分校AMP 實驗室
2、2014年,Spark 成爲 Apache 的頂級項目
Spark爲什麼會流行
原因1:優秀的數據模型和計算抽象
Spark將多個RDD之間的中間數據寫入內存,MR將中間數據寫入硬盤,內存比硬盤速度快
原因2:完善的生態圈
Spark 能夠進行離線計算、交互式查詢(快速查詢)、實時計算、機器學習、圖計算等
Spark Core:Spark基本功能
Spark SQL : 操作結構化數據
Spark Streaming: 對實時數據進行流式計算
Spark MLlib: 機器學習
GraphX:用於圖計算
Hadoop與Spark的對比
|
Hadoop |
Spark |
類型 |
基礎平臺, 包含計算, 存儲, 調度 |
分佈式計算工具 |
場景 |
大規模數據集上的批處理 |
迭代計算, 交互式計算, 流計算 |
價格 |
對機器要求低, 便宜 |
對內存有要求, 相對較貴 |
編程範式 |
Map+Reduce, API 較爲底層, 算法適應性差 |
RDD組成DAG有向無環圖, API 較爲頂層, 方便使用 |
數據存儲結構 |
MapReduce中間計算結果存在HDFS磁盤上, 延遲大 |
RDD中間運算結果存在內存中 , 延遲小 |
運行方式 |
Task以進程方式維護, 任務啓動慢 |
Task以線程方式維護, 任務啓動快 |
Spark運行模式
1.local本地模式(單機)--開發測試使用
2.standalone獨立集羣模式--開發測試使用
3.standalone-HA高可用模式--生產環境使用
4.on yarn集羣模式--生產環境使用
5.on mesos集羣模式--國內使用較少
6.on cloud集羣模式--中小公司未來會更多的使用雲服務
Spark安裝部署
1、local本地模式安裝部署
第一步:上傳並解壓
第二步:開箱即用(不需要修改配置)
啓動spark
./spark-shell
沒有參數表示 等於 local[*]
./spark-shell --master local[*]
Local表示在本機運行。[*]表示可用的所有資源
./spark-shell --master local[n]
Local表示在本機運行。[n]表示使用n個線程
Spark初體驗
1、讀取本地數據計算WordCount
val textFile = sc.textFile("file:///opt/tt.txt")
val counts = textFile.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
2、讀取hdfs集羣數據計算WordCount
val textFile = sc.textFile("hdfs://node01:8020/tt.txt")
val counts = textFile.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://node01:8020/ttt")
standalone集羣模式
第一步:軟件上傳並解壓
第二步:
修改配置文件
export JAVA_HOME=${JAVA_HOME}
export SPARK_MASTER_HOST=node01
export SPARK_MASTER_PORT=7077
修改slaves
node02
node03
第三步:軟件包的分發
scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node02:$PWD
scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node03:$PWD
第四步:啓動集羣
./start-all.sh –master spark://node01:7077
第五步:驗證
sc.textFile("hdfs://node01:8020/tt.txt")
.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
.saveAsTextFile("hdfs://node01:8020/tttt")
standalone-HA高可用模式(不用的話就不需要去配置)
HA解決master的單點問題
第一步:上傳解壓
第二步:修改配置文件
export JAVA_HOME=${JAVA_HOME}
export SPARK_MASTER_PORT=7077
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zooke
eper.url=node01:2181,node02:2181,node03:2181 -Dspark.deploy.zookeeper.dir=/spark"
"../conf/spark-env.sh" 67L, 3966C written
第三步:同步更新的配置到其他節點
scp spark-env.sh node02:/$PWD
scp spark-env.sh node03:/$PWD
第四步:啓動zookeeper
第五步:啓動spark
啓動集羣 ./start-all.sh
啓動第二個Master(node02/node03)
第六步:驗證HA
on yarn集羣模式
前期準備
- hadoop安裝部署好
2、安裝部署單機版本的spark
第一步:安裝包的上傳並解壓
第二步:修改配置文件
export JAVA_HOME=${JAVA_HOME}
export SPARK_MASTER_HOST=node01
export SPARK_MASTER_PORT=7077
export HADOOP_CONF_DIR=/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
第三步測試
使用spark-submits
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 2 \
--queue default \
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/examples/jars/spark-examples_2.11-2.2.0.jar \
10
參數的說明
--master local [n]/[*]/spark://HOST:PORT/YARN 指定 Master 的地址
--name "appName" 指定程序運行的名稱
--class 程序的main方法所在的類
--jars xx.jar 程序額外使用的 jar 包
--driver-memory 512m Driver運行所需要的內存, 默認1g
--executor-memory 2g 指定每個 executor 可用內存爲 2g, 默認1g
--executor-cores 1 指定每一個 executor 可用的核數
--total-executor-cores 2 指定整個集羣運行任務使用的 cup 核數爲 2 個
--queue default 指定任務的對列
--deploy-mode 指定運行模式(client/cluster)
Spark編寫代碼
- 創建一個 Sparkconf對象,設置app名稱
- 創建一個SparkContext,
- 讀取數據,對數據進行計算
- 保存數據
將代碼導出成爲jar文件,將jar文件添加到集羣,使用sparkSubmit提交任務到集羣。
SparkCore
什麼是RDD
彈性分佈式數據集(數據存儲在內存)
彈性的,RDD中的數據可以保存在內存中或者磁盤裏面
分佈式存儲的,可以用於分佈式計算
集合,可以存放很多元素
一個不可變、可分區、裏面的元素可並行計算的集合
RDD的主要屬性
1、數據集的基本組成單位是一組分片(Partition)或一個分區(Partition)列表
每個分片都會被一個計算任務處理,分片數決定並行度。
用戶可以在創建RDD時指定RDD的分片個數,如果沒有指定,那麼就會採用默認值(默認值2)。
2、一個函數會被作用在每一個分區。
Spark中RDD的計算是以分區爲單位的,函數會被作用到每個分區上
3、一個RDD會依賴於其他多個RDD。
RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似於流水線一樣的前後依賴關係。在部分分區數據丟失時,Spark可以通過這個依賴關係重新計算丟失的分區數據,而不是對RDD的所有分區進行重新計算。(Spark的容錯機制)
4.KV類型的RDD會有一個Partitioner函數
非key-value的RDD的Parititioner的值是None,Partitioner函數決定了RDD本身的分區數量,也決定了parent RDD Shuffle輸出時的分區數量。
5、每個RDD維護一個列表,每個Partition的位置(preferred location)存儲在一個列表中。
RDD-API
創建RDD
1、由外部存儲系統的數據集創建
val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")
2、通過已有的RDD經過算子轉換生成新的RDD
val rdd2=rdd1.flatMap(_.split(" "))
3、由一個已經存在的Scala集合創建
方法1 sc.parallelize(Array(1,2,3,4,5,6,7,8))
方法2 sc.makeRDD(List(1,2,3,4,5,6,7,8))
makeRDD方法底層調用了parallelize方法
RDD的方法/算子分類
RDD的算子分爲兩類:
1.Transformation轉換操作:返回一個新的RDD
2.Action動作操作:返回值不是RDD(無返回值或返回其他的)
問答題:判斷一個算子是Transformation??還是Action??
若返回值的類型是一個RDD那麼這個算子就是Transformation,反之就是Action
如何理解spark的惰性計算?
RDD中的所有轉換都是惰性求值/延遲執行的,也就是說並不會直接計算。
遇到Action動作時,這些轉換纔會真正運行
之所以使用惰性求值/延遲執行,是因爲這樣可以在Action時對RDD操作形成DAG有向無環圖進行Stage的劃分和並行優化,這種設計讓Spark更加有效率地運行。
Transformation轉換算子
轉換 |
含義 |
map(func) |
返回一個新的RDD,該RDD由每一個輸入元素經過func函數轉換後組成 |
filter(func) |
返回一個新的RDD,該RDD由經過func函數計算後返回值爲true的輸入元素組成 |
flatMap(func) |
類似於map,但是每一個輸入元素可以被映射爲0或多個輸出元素(所以func應該返回一個序列,而不是單一元素) |
mapPartitions(func) |
類似於map,但獨立地在RDD的每一個分片上運行,因此在類型爲T的RDD上運行時,func的函數類型必須是Iterator[T] => Iterator[U] |
mapPartitionsWithIndex(func) |
類似於mapPartitions,但func帶有一個整數參數表示分片的索引值,因此在類型爲T的RDD上運行時,func的函數類型必須是 (Int, Interator[T]) => Iterator[U] |
sample(withReplacement, fraction, seed) |
根據fraction指定的比例對數據進行採樣,可以選擇是否使用隨機數進行替換,seed用於指定隨機數生成器種子 |
union(otherDataset) |
對源RDD和參數RDD求並集後返回一個新的RDD |
intersection(otherDataset) |
對源RDD和參數RDD求交集後返回一個新的RDD |
distinct([numTasks])) |
對源RDD進行去重後返回一個新的RDD |
groupByKey([numTasks]) |
在一個(K,V)的RDD上調用,返回一個(K, Iterator[V])的RDD |
reduceByKey(func, [numTasks]) |
在一個(K,V)的RDD上調用,返回一個(K,V)的RDD,使用指定的reduce函數,將相同key的值聚合到一起,與groupByKey類似,reduce任務的個數可以通過第二個可選的參數來設置 |
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) |
|
sortByKey([ascending], [numTasks]) |
在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD |
sortBy(func,[ascending], [numTasks]) |
與sortByKey類似,但是更靈活 |
join(otherDataset, [numTasks]) |
在類型爲(K,V)和(K,W)的RDD上調用,返回一個相同key對應的所有元素對在一起的(K,(V,W))的RDD |
cogroup(otherDataset, [numTasks]) |
在類型爲(K,V)和(K,W)的RDD上調用,返回一個(K,(Iterable<V>,Iterable<W>))類型的RDD |
cartesian(otherDataset) |
笛卡爾積 |
pipe(command, [envVars]) |
對rdd進行管道操作 |
coalesce(numPartitions) |
減少 RDD 的分區數到指定值。在過濾大量數據之後,可以執行此操作 |
repartition(numPartitions) |
重新給 RDD 分區 |
Action算子
動作 |
含義 |
reduce(func) |
通過func函數聚集RDD中的所有元素,這個功能必須是可交換且可並聯的 |
collect() |
在驅動程序中,以數組的形式返回數據集的所有元素 |
count() |
返回RDD的元素個數 |
first() |
返回RDD的第一個元素(類似於take(1)) |
take(n) |
返回一個由數據集的前n個元素組成的數組 |
takeSample(withReplacement,num, [seed]) |
返回一個數組,該數組由從數據集中隨機採樣的num個元素組成,可以選擇是否用隨機數替換不足的部分,seed用於指定隨機數生成器種子 |
takeOrdered(n, [ordering]) |
返回自然順序或者自定義順序的前 n 個元素 |
saveAsTextFile(path) |
將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統,對於每個元素,Spark將會調用toString方法,將它裝換爲文件中的文本 |
saveAsSequenceFile(path) |
將數據集中的元素以Hadoop sequencefile的格式保存到指定的目錄下,可以使HDFS或者其他Hadoop支持的文件系統。 |
saveAsObjectFile(path) |
將數據集的元素,以 Java 序列化的方式保存到指定的目錄下 |
countByKey() |
針對(K,V)類型的RDD,返回一個(K,Int)的map,表示每一個key對應的元素個數。 |
foreach(func) |
在數據集的每一個元素上,運行函數func進行更新。 |
foreachPartition(func) |
在數據集的每一個分區上,運行函數func |
統計操作
算子 |
含義 |
count |
個數 |
mean |
均值 |
sum |
求和 |
max |
最大值 |
min |
最小值 |
variance |
方差 |
sampleVariance |
從採樣中計算方差 |
stdev |
標準差:衡量數據的離散程度 |
sampleStdev |
採樣的標準差 |
stats |
查看統計結果 |
repartition可以增加和減少rdd中的分區數,
coalesce默認減少rdd分區數,增加rdd分區數不會生效。
不管增加還是減少分區數原rdd分區數不變,變的是新生成的rdd的分區數
分區原則
1.啓動的時候指定的CPU核數確定了一個參數值:
spark.default.parallelism=指定的CPU核數(集羣模式最小2)
2.對於Scala集合調用parallelize(集合,分區數)方法,
如果沒有指定分區數,就使用spark.default.parallelism,
如果指定了就使用指定的分區數(不要指定大於spark.default.parallelism)
3.對於textFile(文件,分區數) defaultMinPartitions
如果沒有指定分區數sc.defaultMinPartitions=min(defaultParallelism,2)
如果指定了就使用指定的分區數sc.defaultMinPartitions=指定的分區數
rdd的分區數
對於本地文件:
rdd的分區數 = max(本地file的分片數, sc.defaultMinPartitions)
對於HDFS文件:
rdd的分區數 = max(hdfs文件的block數目, sc.defaultMinPartitions)
所以如果分配的核數爲多個,且從文件中讀取數據創建RDD,即使hdfs文件只有1個切片,最後的Spark的RDD的partition數也有可能是2
rdd數據持久化
redis數據持久化什麼作用?(將內存中的數據寫入到硬盤中,進行永久保存)
防止數據丟失!
Rdd數據持久化什麼作用?
1、對多次使用的rdd進行緩存,緩存到內存,當後續頻繁使用時直接在內存中讀取緩存的數據,不需要重新計算。
2、將RDD結果寫入硬盤(容錯機制),當RDD丟失數據時,或依賴的RDD丟失數據時,可以使用持久化到硬盤的數據恢復。
持久化/緩存(內存)
緩存方法
Persist
Cache
這兩個方法被調用時立即緩存,而是觸發後面的action時
代碼
val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")
val rdd2 = rdd1.flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_)
rdd2.cache
rdd2.sortBy(_._2,false).collect//觸發action,會去讀取HDFS的文件,rdd2會真正執行持久化
rdd2.sortBy(_._2,false).collect
緩存級別
掌握的級別
MEMORY_ONLY(默認):數據寫入內存,內存不足,部分分區數據不緩存。
MEMORY_AND_DISK:數據寫入內存,內存不足寫入磁盤。
DISK_ONLY:數據寫入硬盤。
總結:
1.RDD持久化/緩存的目的是爲了提高後續操作的速度
2.緩存的級別有很多,默認只存在內存中,開發中使用memory_and_disk
3.只有執行action操作的時候纔會真正將RDD數據進行持久化/緩存
4.實際開發中如果某一個RDD後續會被頻繁的使用,可以將該RDD進行持久化/緩存
RDD持久化(容錯機制Checkpoint)
將數據寫入HDFS,利用HDFS永久存儲。
操作過程
- 設置持久化的存儲路徑
- 調用checkpoint()進行數據的保存
SparkContext.setCheckpointDir("目錄") //HDFS的目錄
RDD.checkpoint()
代碼:
sc.setCheckpointDir("hdfs://node01:8020/ckpdir")
val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
rdd1.checkpoint()
rdd1.collect
持久化結果
cache和Checkpoint的區別
位置
Persist 和 Cache將數據保存在內存
Checkpoint將數據保存在HDFS
生命週期
Persist 和 Cache 程序結束後會被清除或手動調用unpersist方法。
Checkpoint永久存儲不會被刪除。
RDD依賴關係(血統Lineage)
Persist和Cache,不會丟掉RDD間的依賴鏈/依賴關係
Checkpoint會斬斷依賴鏈
RDD的依賴關係
窄依賴:父RDD的一個分區只會被子RDD的一個分區依賴(圖一)
寬依賴:父RDD的一個分區會被子RDD的多個分區依賴(涉及到shuffle)(圖二)
爲什麼劃分寬窄依賴?
1.對於寬依賴
是劃分Stage的依據(目的是實現並行化計算)
2.對於窄依賴
Spark Stage可以並行計算(並行計算速度快)
DAG介紹
DAG:指的是數據轉換執行的過程,有方向,無閉環(其實就是RDD執行的流程)
DAG邊界
開始:通過SparkContext創建的RDD
結束:觸發Action,一旦觸發Action就形成了一個完整的DAG
說明:
一個Spark應用中可以有一到多個DAG,取決於觸發了多少次Action
一個DAG中會有不同的階段/stage,劃分階段/stage的依據就是寬依賴
一個階段/stage中可以有多個Task,一個分區對應一個Task
爲什麼要劃分Stage??
爲了實現並行化計算,提高計算效率。
總結
Spark會根據shuffle/寬依賴使用回溯算法來對DAG進行Stage劃分,從後往前,遇到寬依賴就斷開,遇到窄依賴就把當前的RDD加入到當前的stage/階段中
哪些算法是寬依賴??
??????
Spark概念介紹
1.Application:指的是用戶編寫的Spark應用程序/代碼,包含了Driver功能代碼和分佈在集羣中多個節點上運行的Executor代碼。
2.Driver:Spark中的Driver即運行上述Application的Main()函數並且創建SparkContext,SparkContext負責和ClusterManager通信,進行資源的申請、任務的分配和監控等
3.Cluster Manager:指的是在集羣上獲取資源的外部服務,Standalone模式下由Master負責,Yarn模式下ResourceManager負責;
4.Executor:是運行在工作節點Worker上的進程,負責運行任務,併爲應用程序存儲數據,是執行分區計算任務的進程;
5.RDD:Resilient Distributed Dataset彈性分佈式數據集,是分佈式內存的一個抽象概念;
6.DAG:Directed Acyclic Graph有向無環圖,反映RDD之間的依賴關係和執行流程;
7.Job:作業,按照DAG執行就是一個作業;Job==DAG
8.Stage:階段,是作業的基本調度單位,同一個Stage中的Task可以並行執行,多個Task組成TaskSet任務集
9.Task:任務,運行在Executor上的工作單元,一個Task計算一個分區,包括pipline上的一系列操作
Spark執行任務的基本流程(簡答-Yarn 缺)
1.當一個Spark應用被提交時,首先需要爲這個Spark Application構建基本的運行環境,即由任務控制節點(Driver)創建一個SparkContext,
2.SparkContext向資源管理器註冊並申請運行Executor資源;
3.資源管理器爲Executor分配資源並啓動Executor進程,Executor運行情況將隨着心跳發送到資源管理器上;
4.SparkContext根據RDD的依賴關係構建成DAG圖,並提交給DAGScheduler進行解析劃分成Stage,並把該Stage中的Task組成Taskset發送給TaskScheduler。
5.TaskScheduler將Task發放給Executor運行,同時SparkContext將應用程序代碼發放給Executor。
6.Executor將Task丟入到線程池中執行,把執行結果反饋給任務調度器,然後反饋給DAG調度器,運行完畢後寫入數據並釋放所有資源。
總結(master,缺Yarn)
1.Spark應用被提交-->SparkContext向資源管理器註冊並申請資源 (??) -->啓動Executor
2.RDD-->構建DAG-->DAGScheduler劃分Stage形成TaskSet-->TaskScheduler提交Task-->Worker上的Executor執行Task
RDD累加器和廣播變量
1.累加器accumulators:累加器支持在所有不同節點之間進行累加計算
package cn.itcast.core
|
2.廣播變量broadcast variables:廣播變量用來把變量在所有節點的內存之間進行共享,在每個機器上緩存一個只讀的變量,而不是爲機器上的每個任務都生成一個副本。
import org.apache.spark.broadcast.Broadcast
//先將數據添加到廣播變量 //在廣播變量中獲取數據 |
通常在兩個表(一個大表一個小表,將小表廣播出去)的join時會用到廣播變量。
txt文本用sc將數據寫入到msql和從msql中讀取
|
txt文本用sc將數據寫入到hbase和從hbase中讀取
|