spark綜合問題

1.什麼是spark?
spark是基於內存計算的通用大數據並行計算框架,是一個快速、通用可擴展的大數據分析引擎。它給出了大一統的軟件開發棧,適用於不同場合的分佈式場景,如批處理、迭代算法、交互式查詢、流處理、機器學習和圖計算。
2.Spark生態系統?
SparkCore:spark的核心計算 主要Rdd
SparkSQL:提供了類sql方式操作結構化半結構化數據。對歷史數據進行交互式查詢。(即席查詢:用戶根據自己的需求,自定義查詢)

SparkStreaming:提供了近乎實時的流式數據處理,與storm相比有更高的吞吐量。(實時計算 目前實時計算框架有哪些? storm、sparkstreaming、flink)

SparkMl:提供了常見的機器學習算法庫,包括分類、迴歸、聚類、協同工過濾(個性推薦:用戶畫像)等,還提供模型評估、數據處理等額外功能,使得機器學習能夠更加方便的在分佈式大數據環境下,快速的對數據進行處理形成模型後提供在線服務。

Graphx:用來操作圖的程序庫,可以進行並行的圖計算。支持各種常見的圖算法,包括page rank、Triangle Counting等。
3.常見的 分佈式文件系統?
hdfs fastdfs Tachyon TFS GFS S3
4.master資源分配有哪些?
儘量集中 儘量打散
1.Application的調度算法有兩種,一種是spreadOutApps,另一種是非spreadOutApps。

2.spreadOutApps,會將每個Application要啓動的executor都平均分配到各個worker上去。(比如有10個worker,20個cpu core要分配,那麼實際會循環兩遍worker,每個worker分配一個core,最後每個worker分配了2個core,這裏的executor數量可能會與spark-submit設置的不一致)

3.非spreadOutApps,將每個Application儘可能分配到儘量少的worker上去。(比如總共有10個worker,每個有10個core,app總共要分配20個core,其實只會分配到兩個worker上,每個worker佔滿10個core,其餘app只能分配到下一個worker,這裏的executor數量可能會與spark-submit設置的不一致)
5.spark 可以代替hadoop 嗎?
spark會替代Hadoop的一部分,會替代Hadoop的計算框架,如mapReduce、Hive查詢引擎,但spark本身不提供存儲,所以spark不能完全替代Hadoop。
6.spark 特點?
6.1 速度快
Spark 使用DAG 調度器、查詢優化器和物理執行引擎,能夠在批處理和流數據獲得很高的性能。根據官方的統計,它的運算速度是hadoop的100x倍
6.2 使用簡單
Spark的易用性主要體現在兩個方面。一方面,我們可以用較多的編程語言來寫我們的應用程序,比如說Java,Scala,Python,R 和 SQL;另一方面,Spark 爲我們提供了超過80個高階操作,這使得我們十分容易地創建並行應用,除此之外,我們也可以使用Scala,Python,R和SQL shells,以實現對Spark的交互
6.3 通用性強
與其說通用性高,還不如說它集成度高,如圖所示:以Spark爲基礎建立起來的模塊(庫)有Spark SQL,Spark Streaming,MLlib(machine learning)和GraphX(graph)。我們可以很容易地在同一個應用中將這些庫結合起來使用,以滿足我們的實際需求。
6.4 到處運行
Spark應用程度可以運行十分多的框架之上。它可以運行在Hadoop,Mesos,Kubernetes,standalone,或者雲服務器上。它有多種多種訪問源數據的方式。可以用standalone cluster模式來運行Spark應用程序,並且其應用程序跑在Hadoop,EC2,YARN,Mesos,或者Kubernates。對於訪問的數據源,我們可以通過使用Spark訪問HDFS,Alluxio,Apache Cassandra,HBase,Hive等多種數據源。
7.DAG

有向無環圖
"有向"指的是有方向,準確的說應該是同一個方向,"無環"則指夠不成閉環。在DAG中,沒有區塊的概念,他的組成單元是一筆筆的交易,每個單元記錄的是單個用戶的交易,這樣就省去了打包出塊的時間。驗證手段則依賴於後一筆交易對前一筆交易的驗證,換句話說,你要想進行一筆交易,就必須要驗證前面的交易,具體驗證幾個交易,根據不同的規則來進行。這種驗證手段,使得DAG可以異步併發的寫入很多交易,並最終構成一種拓撲的樹狀結構,能夠極大地提高擴展性。

8.spark 開發語言選擇?
java scala python
9.什麼是Rdd?
Spark 中最基本的數據抽象是 RDD。

RDD:彈性分佈式數據集 (Resilient Distributed DataSet)。
RDD 有三個基本特性

這三個特性分別爲:分區,不可變,並行操作。
a, 分區

每一個 RDD 包含的數據被存儲在系統的不同節點上。邏輯上我們可以將 RDD 理解成一個大的數組,數組中的每個元素就代表一個分區 (Partition) 。

在物理存儲中,每個分區指向一個存儲在內存或者硬盤中的數據塊 (Block) ,其實這個數據塊就是每個 task 計算出的數據塊,它們可以分佈在不同的節點上。

所以,RDD 只是抽象意義的數據集合,分區內部並不會存儲具體的數據,只會存儲它在該 RDD 中的 index,通過該 RDD 的 ID 和分區的 index 可以唯一確定對應數據塊的編號,然後通過底層存儲層的接口提取到數據進行處理。

在集羣中,各個節點上的數據塊會儘可能的存儲在內存中,只有當內存沒有空間時纔會放入硬盤存儲,這樣可以最大化的減少硬盤 IO 的開銷。
b,不可變

不可變性是指每個 RDD 都是隻讀的,它所包含的分區信息是不可變的。由於已有的 RDD 是不可變的,所以我們只有對現有的 RDD 進行轉化 (Transformation) 操作,才能得到新的 RDD ,一步一步的計算出我們想要的結果。

這樣會帶來這樣的好處:我們在 RDD 的計算過程中,不需要立刻去存儲計算出的數據本身,我們只要記錄每個 RDD 是經過哪些轉化操作得來的,即:依賴關係,這樣一方面可以提高計算效率,一方面是錯誤恢復會更加容易。如果在計算過程中,第 N 步輸出的 RDD 的節點發生故障,數據丟失,那麼可以根據依賴關係從第 N-1 步去重新計算出該 RDD,這也是 RDD 叫做"彈性"分佈式數據集的一個原因。
c,並行操作

因爲 RDD 的分區特性,所以其天然支持並行處理的特性。即不同節點上的數據可以分別被處理,然後生成一個新的 RDD。
10.Rdd的屬性?

  • 1) A list of partitions

一個分區列表,一個rdd有多個分區,後期spark任務計算是以分區爲單位,一個分區就對應上一個task線程。 通過val rdd1=sc.textFile(文件) 如果這個文件大小的block個數小於等於2,它產生的rdd的分區數就是2 如果這個文件大小的block個數大於2,它產生的rdd的分區數跟文件的block相同

  • 2)A function for computing each split

由一個函數計算每一個分片 比如: rdd2=rdd1.map(x=>(x,1)) ,這裏指的就是每個單詞計爲1的函數

  • 3)A list of dependencies on other RDDs

一個rdd會依賴於其他多個rdd,這裏就涉及到rdd與rdd之間的依賴關係,後期spark任務的容錯機制就是根據這個特性而來。 比如: rdd2=rdd1.map(x=>(x,1)) rdd2的結果是通過rdd1調用了map方法生成,那麼rdd2就依賴於rdd1的結果 對其他RDD的依賴列表,依賴還具體分爲寬依賴和窄依賴,但並不是所有的RDD都有依賴。

  • 4)Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)

(可選項) 對於kv類型的rdd纔會有分區函數(必須要產生shuffle),對於不是kv類型的rdd分區函數是None。 分區函數的作用:它是決定了原始rdd的數據會流入到下面rdd的哪些分區中。 spark的分區函數有2種:第一種hashPartitioner(默認值), 通過 key.hashcode % 分區數=分區號 第二種RangePartitioner,是基於一定的範圍進行分區。

  • 5)Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)

(可選項) 一組最優的數據塊的位置,這裏涉及到數據的本地性和數據位置最優 spark後期在進行任務調度的時候,會優先考慮存有數據的worker節點來進行任務的計算。大大減少數據的網絡傳輸,提升性能。
11.分片的個數由什麼決定?

默認 是手動 ,外部的可從hdfs kafka

text文件分片(sc.textFile爲例):

def textFile(
  path: String,
  minPartitions: Int = defaultMinPartitions): RDD[String] = withScope {
assertNotStopped()
hadoopFile(path, classOf[TextInputFormat]  /* 數據文件的輸入格式 :org.apache.hadoop.mapred.TextInputFormat */
, classOf[LongWritable], classOf[Text],
  minPartitions).map(pair => pair._2.toString).setName(path)
}
 hadoopFile方法生成HadoopRDD
 HadoopRDD(
  this,
  confBroadcast,
  Some(setInputPathsFunc),
  inputFormatClass,
  keyClass,
  valueClass,
  minPartitions) /* minPartitions爲生成該RDD的最小分片數,表示該RDD的分片數最小值,默認爲2*/
  .setName(path)

在執行action方法(如count)時,spark應用才真正開始計算,通過調用rdd.partitions.length計算出分片數

    def runJob[T, U: ClassTag](rdd: RDD[T], func: Iterator[T] => U): Array[U] = {
runJob(rdd, func, 0 until rdd.partitions.length)
}

通過跟蹤該方法可以看出該函數最終會調用到HadoopRDD的getPartitions方法,在該方法中通過inputFormat的getSplit方法計算分片數

    getInputFormat(jobConf).getSplits(jobConf, minPartitions)

TextInputFormat繼承至FileInputFormat,FileInputFormat的getSplit方法網上有許多分析,這裏不再展開,大致的原理是根據文件個數,傳入的minpartitions,mapreduce.input.fileinputformat.split.minsize等參數計算出分片數。

hbase表分片

在讀取HBase數據時,沒有類似textFile的接口的封裝,可調用如下接口生成給予hbase數據的RDD,

val hBaseRDD = sc.newAPIHadoopRDD(conf, 
classOf[TableInputFormat], /*該類的全類名爲:  org.apache.hadoop.hbase.mapreduce.TableInputFormat */
  classOf[org.apache.hadoop.hbase.io.ImmutableBytesWritable],  
  classOf[org.apache.hadoop.hbase.client.Result])  
  
  該方法生成new NewHadoopRDD(this, fClass, kClass, vClass, jconf)

在執行action操作時,同樣調用到rdd.partitions方法,跟蹤至newHadoopRDD之後,發現調用到

inputFormat.getSplits(new JobContextImpl(_conf, jobId))

查看對應的getSplits方法可以看出:

默認情況下(hbase.mapreduce.input.autobalance的值爲false)hbase表如果存在多個region,則每個region設置爲一個split。

如果設置了開啓均衡(設置hbase.mapreduce.input.autobalance的值爲true:在hbase的region大小不均衡引發的數據傾斜,將導致不同的region處理耗時較多,該參數爲了解決此場景),則會在每個region對應一個split的基礎上,將較小(小於平均大小)的region進行合併作爲一個split,較大(大於平均size的三倍(其中三可配置))的region拆分爲兩個region。

   splits僞代碼如下(源碼可參考TableInputFormatBase.calculateRebalancedSplits):
    
    while ( i < splits.size)
    {
        if(splits(i).size > averagesize * 3) {
        if(! splitAble)
            resultsplits.add(split(i))
        else{
            (split1,split2) = Split(splits(i))
            resultsplits.add(split1)
            resultsplits.add(split2)
        }
        i++ 
        }
        else if(splits(i).size > averagesize) {
            resultsplits.add(split(i))
            i++
        }else{
            startKey = split(i).getStartRow
            i++;
            while(totalSize + splits(i).size < averagesize * 3){
                totalSize += splits(i).size
                endKey = splits(i).getEndRow
            }
            resultsplits.add(new TableSplit(startKey,endKey,*))
        }
    }

Kafka數據的分片

Spark框架在讀取Kafka消息時,將Kafka數據抽象爲KafkaRDD(SparkStreaming)或者KafkaSourceRDD(StructedStreaming),查看對應RDD的getPartitions方法和定義:
KafkaSourceRDD:

    override def getPartitions: Array[Partition] = {
offsetRanges.zipWithIndex.map { case (o, i) => new KafkaSourceRDDPartition(i, o) }.toArray
 }
 offsetRanges的數據結構爲
 private[kafka010] case class KafkaSourceRDDOffsetRange(
topicPartition: TopicPartition,
fromOffset: Long,
untilOffset: Long,
preferredLoc: Option[String]) 

可以看出partition個數爲對應的TopicPartition的個數
KafkaRDD

override def getPartitions: Array[Partition] = {
    offsetRanges.zipWithIndex.map { case (o, i) =>
        new KafkaRDDPartition(i, o.topic, o.partition, o.fromOffset, o.untilOffset)
    }.toArray
  }
  offsetRanges數據結構爲:
  final class OffsetRange private(
        val topic: String,
        val partition: Int,
        val fromOffset: Long,
        val untilOffset: Long) 

可以看出partition個數爲對應的partition的個數
12.分片的意義?
增加並行度
當然也不是並行越高越好,根據資源而定
13.spark 的容錯?
1、Lineage機制
Lineage簡介

相比其他系統的細顆粒度的內存數據更新級別的備份或者LOG機制,RDD的Lineage記錄的是粗顆粒度的特定數據Transformation操作(如filter、map、join等)行爲。當這個RDD的部分分區數據丟失時,它可以通過Lineage獲取足夠的信息來重新運算和恢復丟失的數據分區。因爲這種粗顆粒的數據模型,限制了Spark的運用場合,所以Spark並不適用於所有高性能要求的場景,但同時相比細顆粒度的數據模型,也帶來了性能的提升。
2、Checkpoint機制

我們應該都很熟悉 checkpoint 這個概念, 就是把內存中的變化刷新到持久存儲,斬斷依賴鏈 在存儲中 checkpoint 是一個很常見的概念, 舉幾個例子

  • 數據庫 checkpoint 過程中一般把內存中的變化進行持久化到物理頁, 這時候就可以斬斷依賴鏈, 就可以把 redo 日誌刪掉了, 然後更新下檢查點,

  • hdfs namenode 的元數據 editlog, Secondary namenode 會把 edit log 應用到 fsimage, 然後刷到磁盤上, 也相當於做了一次 checkpoint, 就可以把老的 edit log 刪除了。

  • spark streaming 中對於一些 有狀態的操作, 這在某些 stateful 轉換中是需要的,在這種轉換中,生成 RDD 需要依賴前面的 batches,會導致依賴鏈隨着時間而變長。爲了避免這種沒有盡頭的變長,要定期將中間生成的 RDDs 保存到可靠存儲來切斷依賴鏈, 必須隔一段時間進行一次進行一次 checkpoint。

cache 和 checkpoint 是有顯著區別的, 緩存把 RDD 計算出來然後放在內存中, 但是RDD 的依賴鏈(相當於數據庫中的redo 日誌), 也不能丟掉, 當某個點某個 executor 宕了, 上面cache 的RDD就會丟掉, 需要通過 依賴鏈重放計算出來, 不同的是, checkpoint 是把 RDD 保存在 HDFS中, 是多副本可靠存儲,所以依賴鏈就可以丟掉了,就斬斷了依賴鏈, 是通過複製實現的高容錯。但是有一點要注意, 因爲checkpoint是需要把 job 重新從頭算一遍, 最好先cache一下, checkpoint就可以直接保存緩存中的 RDD 了, 就不需要重頭計算一遍了, 對性能有極大的提升。
簡單來說,就是把重要的Rdd存儲在hdfs中,與lineage相比,省時
14.創建Rdd的方式?

  1. 由一個已經存在的Scala集合創建。
val rdd: RDD[Int] = sc.parallelize(Arrscay(1,2,3,4,5))

  1. 由外部存儲系統的文件創建。
    包括本地的文件系統,還有所有Hadoop支持的數據集,比如HDFS、Cassandra、HBase等。
val rdd2 = sc.textFile("/words.txt")

  1. 已有的RDD經過算子轉換生成新的RDD
 val rdd3=rdd2.flatMap(_.split(" "))

15.spark 算子分爲? 區別? 寫出10個以上常用的算子
1.從大的方向來說,spark算子可以分兩類:
1)Transformation 變換/轉換算子:這種變換並不觸發提交作業,完成作業中間過程處理。Transformation 操作是延遲計算的,也就是說從一個RDD 轉換生成另一個 RDD 的轉換操作不是馬上執行,需要等到有 Action 操作的時候纔會真正觸發運算。
2)Action算子:Action 行動算子:這類算子會觸發 SparkContext 提交 Job 作業。Action 算子會觸發 Spark 提交作業(Job),並將數據輸出 Spark系統。
2.從小的方向分,可以分爲三類:
1)Value數據類型的Transformation算子,這種變換並不觸發提交作業,針對處理的數據項是Value型的數據。
2)Key-Value數據類型的Transfromation算子,這種變換並不觸發提交作業,針對處理的數據項是Key-Value型的數據對。
3)Action算子,這類算子會觸發SparkContext提交Job作業。
常見算子:
spark常見算子
16.如何產生job?
Spark的Job來源於用戶執行action操作,就是從RDD中獲取結果的操作,而不是將一個RDD轉換成另一個RDD的transformation操作。
17.map 和mappartition 的區別?

  • map():每次處理一條數據

  • mapPartition():每次處理一個分區的數據,這個分區的數據處理完後,原RDD中分區的數據才能釋放,可能導致OOM

    當內存空間較大的時候建議使用mapPartition(),以提高處理效率

18.repartition 和 coalesce 的區別?

  • coalesce重新分區,可以選擇是否進行shuffle過程。由參數shuffle: Boolean = false/true決定
  • repartition實際上是調用的coalesce,默認是進行shuffle的
    一般情況下,repartition是 加分區操作,coalesce 是減分區操作

19.什麼是寬依賴 窄依賴?
寬依賴:父RDD的分區被子RDD的多個分區使用 例如 groupByKey、reduceByKey、sortByKey等操作會產生寬依賴,會產生shuffle
窄依賴:父RDD的每個分區都只被子RDD的一個分區使用 例如map、filter、union等操作會產生窄依賴
20.寬依賴的算子有哪些?
bykey的算子、repartition、部分join算子
21.reduceByKey aggregateByKey combineByKey 區別?

  • aggregateByKey()是先對每個partition中的數據根據不同的Key進行aggregate,然後將結果進行shuffle,完成各個partition之間的aggregate。
  • reduceByKey()也是先在單臺機器中計算,再將結果進行shuffle,減小運算量
  • combineByKey 與aggregateByKey類似,都調用了combineByKeyWithClassTag,在aggregateByKey中的

22.使用什麼可以代替 join ?
廣播變量 + map + filter
23.持久化?
RDD通過persist方法或cache方法可以將前面的計算結果緩存,默認情況下 persist() 會把數據以序列化的形式緩存在 JVM 的堆空間中。
但是並不是這兩個方法被調用時立即緩存,而是觸發後面的action時,該RDD將會被緩存在計算節點的內存中,並供後面重用。
在這裏插入圖片描述
通過查看源碼發現cache最終也是調用了persist方法,默認的存儲級別都是僅在內存存儲一份,Spark的存儲級別還有好多種,存儲級別在object StorageLevel中定義的。
在這裏插入圖片描述
24.容錯?
lineage、checkpoint
25.共享變量?
累加器
累加器用來對信息進行聚合,通常在向 Spark傳遞函數時,比如使用 map() 函數或者用 filter() 傳條件時,可以使用驅動器程序中定義的變量,但是集羣中運行的每個任務都會得到這些變量的一份新的副本,更新這些副本的值也不會影響驅動器中的對應變量。如果我們想實現所有分片處理時更新共享變量的功能,那麼累加器可以實現我們想要的效果。
系統累加器
針對一個輸入的日誌文件,如果我們想計算文件中所有空行的數量,我們可以編寫以下程序:

scala> val notice = sc.textFile("./NOTICE")
notice: org.apache.spark.rdd.RDD[String] = ./NOTICE MapPartitionsRDD[40] at textFile at <console>:32

scala> val blanklines = sc.accumulator(0)
warning: there were two deprecation warnings; re-run with -deprecation for details
blanklines: org.apache.spark.Accumulator[Int] = 0

scala> val tmp = notice.flatMap(line => {
     |    if (line == "") {
     |       blanklines += 1
     |    }
     |    line.split(" ")
     | })
tmp: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[41] at flatMap at <console>:36

scala> tmp.count()
res31: Long = 3213

scala> blanklines.value
res32: Int = 171

累加器的用法如下所示。
通過在驅動器中調用SparkContext.accumulator(initialValue)方法,創建出存有初始值的累加器。返回值爲 org.apache.spark.Accumulator[T] 對象,其中 T 是初始值 initialValue 的類型。Spark閉包裏的執行器代碼可以使用累加器的 += 方法(在Java中是 add)增加累加器的值。 驅動器程序可以調用累加器的value屬性(在Java中使用value()或setValue())來訪問累加器的值。
注意:工作節點上的任務不能訪問累加器的值。從這些任務的角度來看,累加器是一個只寫變量。
對於要在行動操作中使用的累加器,Spark只會把每個任務對各累加器的修改應用一次。因此,如果想要一個無論在失敗還是重複計算時都絕對可靠的累加器,我們必須把它放在 foreach() 這樣的行動操作中。轉化操作中累加器可能會發生不止一次更新
自定義累加器
自定義累加器類型的功能在1.X版本中就已經提供了,但是使用起來比較麻煩,在2.0版本後,累加器的易用性有了較大的改進,而且官方還提供了一個新的抽象類:AccumulatorV2來提供更加友好的自定義類型累加器的實現方式。實現自定義類型累加器需要繼承AccumulatorV2並至少覆寫下例中出現的方法,下面這個累加器可以用於在程序運行過程中收集一些文本類信息,最終以Set[String]的形式返回。1

package com.atguigu.spark

import org.apache.spark.util.AccumulatorV2
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.JavaConversions._

class LogAccumulator extends org.apache.spark.util.AccumulatorV2[String, java.util.Set[String]] {
  private val _logArray: java.util.Set[String] = new java.util.HashSet[String]()

  override def isZero: Boolean = {
    _logArray.isEmpty
  }

  override def reset(): Unit = {
    _logArray.clear()
  }

  override def add(v: String): Unit = {
    _logArray.add(v)
  }

  override def merge(other: org.apache.spark.util.AccumulatorV2[String, java.util.Set[String]]): Unit = {
    other match {
      case o: LogAccumulator => _logArray.addAll(o.value)
    }

  }

  override def value: java.util.Set[String] = {
    java.util.Collections.unmodifiableSet(_logArray)
  }

  override def copy():org.apache.spark.util.AccumulatorV2[String, java.util.Set[String]] = {
    val newAcc = new LogAccumulator()
    _logArray.synchronized{
      newAcc._logArray.addAll(_logArray)
    }
    newAcc
  }
}

// 過濾掉帶字母的
object LogAccumulator {
  def main(args: Array[String]) {
    val conf=new SparkConf().setAppName("LogAccumulator")
    val sc=new SparkContext(conf)

    val accum = new LogAccumulator
    sc.register(accum, "logAccum")
    val sum = sc.parallelize(Array("1", "2a", "3", "4b", "5", "6", "7cd", "8", "9"), 2).filter(line => {
      val pattern = """^-?(\d+)"""
      val flag = line.matches(pattern)
      if (!flag) {
        accum.add(line)
      }
      flag
    }).map(_.toInt).reduce(_ + _)

    println("sum: " + sum)
    for (v <- accum.value) print(v + "")
    println()
    sc.stop()
  }
}

廣播變量
廣播變量用來高效分發較大的對象。向所有工作節點發送一個較大的只讀值,以供一個或多個Spark操作使用。比如,如果你的應用需要向所有節點發送一個較大的只讀查詢表,甚至是機器學習算法中的一個很大的特徵向量,廣播變量用起來都很順手。 在多個並行操作中使用同一個變量,但是 Spark會爲每個任務分別發送。

scala> val broadcastVar = sc.broadcast(Array(1, 2, 3))
broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(35)

scala> broadcastVar.value
res33: Array[Int] = Array(1, 2, 3)

使用廣播變量的過程如下:
(1) 通過對一個類型 T 的對象調用 SparkContext.broadcast 創建出一個 Broadcast[T] 對象。 任何可序列化的類型都可以這麼實現。
(2) 通過 value 屬性訪問該對象的值(在 Java 中爲 value() 方法)。
(3) 變量只會被髮到各個節點一次,應作爲只讀值處理(修改這個值不會影響到別的節點)。
26.spark 提交方式? 區別?

  • client模式:
    在這裏插入圖片描述
  • cluster模式:
    在這裏插入圖片描述
    client模式與cluster模式的區別:
  1. 啓動集羣nodeManager向ResourceManager彙報資源。

  2. RS掌握了集羣資源。

  3. 客戶端提交application。

  4. 客戶端向ResourceManager申請啓動ApplicationMaster。

  5. ResourceManager收到請求之後隨即找到一臺NodeManager結點啓動ApplicationMaster。

  6. ApplicationMaster啓動之後向ResourceManager申請資源用於啓動Executor。

  7. ResourceManager將申請到的Executor的節點NodeManager返回給ApplicationMaster。

  8. ApplicationMaster連接NodeManager啓動Executor。

  9. Executor啓動字後反向註冊給ApplicationMaster

  10. ApplicationMaster發送task到Executor執行並監控task執行回收結果。
    注意:
    client模式提交任務,會在客戶端看到task的執行情況和結果,當在客戶端提交多個application時,每個application都會啓動自己的Driver,Driver與集羣Worker有大量的通信,會造成客戶端網卡流量激增問題。這種模式適用於程序測試。不適用於生產環境。

    模式提交任務,Driver會在急羣衆隨機一臺Worker上啓動,如果提交多個application時,那麼每個application的Driver會分散到集羣的Worker節點,相當於將client模式的客戶端網卡流量激增問題分散到集羣中。這種模式適用於生產環境。

    因爲cluster模式,隨機分散在Worker節點上創建Driver,由Driver來發送任務到Worker。所以打包的程序任務必須在分散的Worker節點對應的目錄下都存在。
    27.spark 資源調度方式?
    本地模式:
    在這裏插入圖片描述
    standalone模式
    在這裏插入圖片描述
    此模式是spark自帶的集羣模式
    yarn模式:
    Spark客戶端直接連接Yarn,不需要額外構建Spark集羣。有yarn-client和yarn-cluster兩種模式,主要區別在於:Driver程序的運行節點。
    yarn-client:Driver程序運行在客戶端,適用於交互、調試,希望立即看到app的輸出
    yarn-cluster:Driver程序運行在由RM(ResourceManager)啓動的AP(APPMaster)適用於生產環境。
    在這裏插入圖片描述

28.spark 查看日誌的方式?
日誌聚合:較爲常用
分散查看:比較麻煩,到每個節點lo文件查看
webui:到網頁查看
29.spark 優化?
(硬件 配置 代碼)
請看小弟的
大數據優化之spark篇
30.JVM ?
JVM 是 java虛擬機,是用來執行java文件的
包括堆 棧 方法區
棧:裏面有參數 | 局部變量 | 返回地址
8種基本類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配[局部變量]
堆:對象本身、全局變量 [ 成員變量 ]
堆內存內部結構

①. 所有通過new創建的對象的內存都在堆中分配,其大小可以通過-Xmx 和Xms來控制

②. 堆被劃分爲新生代和老年代,新生代又被進一步劃分爲Eden(伊甸園)和Survivor(倖存者)區,老年代,在後面不是堆區是永恆代(方法區)
方法區的概述

①. 所有Java虛擬機線程共享的區域,存儲了類結構相關的信息 [ 成員變量、方法、構造器 ]等

②. 方法區的創建是在Java 虛擬機啓動時被創建

③. 方法區時邏輯上時堆的一個組成部分,但是在不同虛擬機裏頭實現是不一樣的,最典型的就是永久代(PermGen space)和元空間(Metaspace)
注意:方法區時一種規範,而永久代和元空間是它的一種實現方式

④. 方法區存在OOM現象

存儲的東西:
靜態變量+常量+類信息+運行時常量池存在方法區中 

常量池和運行時常量池

①. 常量池,就是一張表,虛擬機指令根據這張常量表找到要執行的類名、方法名、參數類型、字面量等信息

②. 運行時常量池,常量池是 *.class 文件中的,當該類被加載,它的常量池信息就會放入運行時常量池,並把裏面的符號地址變爲真實地址

③. 進入class的目錄,javap -v 類.class
二進制字節碼(類基本信息+常量池+類的方法定義

31.GC的策略?
請參考此文
32.spark 序列化?
有java和kyro,默認是java,但是使用後者更加迅速
KryoSerialization速度快,可以配置爲任何org.apache.spark.serializer的子類。但Kryo也不支持所有實現了 java.io.Serializable 接口的類型,它需要你在程序中 register 需要序列化的類型,以得到最佳性能。
33.spark 配置優先級?
配置文件 < 腳本 < 代碼
34.sparkStreagming 和storm 對比?
兩者並無高下之分,只是應用場景不同。
在這裏插入圖片描述
兩者對比
Spark Streaming僅僅在吞吐量上比Storm要優秀,而吞吐量這一點,也是歷來挺Spark Streaming,貶Storm的人着重強調的。但是問題是,是不是在所有的實時計算場景下,都那麼注重吞吐量?不盡然。因此,通過吞吐量說Spark Streaming強於Storm,不靠譜。

事實上,Storm在實時延遲度上,比Spark Streaming就好多了,前者是純實時,後者是準實時。而且,Storm的事務機制、健壯性 / 容錯性、動態調整並行度等特性,都要比Spark Streaming更加優秀。

Spark Streaming,有一點是Storm絕對比不上的,就是:它位於Spark生態技術棧中,因此Spark Streaming可以和Spark Core、Spark SQL無縫整合,也就意味着,我們可以對實時處理出來的中間數據,立即在程序中無縫進行延遲批處理、交互式查詢等操作。這個特點大大增強了Spark Streaming的優勢和功能。
使用場景
對於Storm來說:
1、建議在那種需要純實時,不能忍受1秒以上延遲的場景下使用,比如實時金融系統,要求純實時進行金融交易和分析
2、此外,如果對於實時計算的功能中,要求可靠的事務機制和可靠性機制,即數據的處理完全精準,一條也不能多,一條也不能少,也可以考慮使用Storm
3、如果還需要針對高峯低峯時間段,動態調整實時計算程序的並行度,以最大限度利用集羣資源(通常是在小型公司,集羣資源緊張的情況),也可以考慮用Storm
4、如果一個大數據應用系統,它就是純粹的實時計算,不需要在中間執行SQL交互式查詢、複雜的transformation算子等,那麼用Storm是比較好的選擇

對於Spark Streaming來說:
1、如果對上述適用於Storm的三點,一條都不滿足的實時場景,即,不要求純實時,不要求強大可靠的事務機制,不要求動態調整並行度,那麼可以考慮使用Spark Streaming
2、考慮使用Spark Streaming最主要的一個因素,應該是針對整個項目進行宏觀的考慮,即,如果一個項目除了實時計算之外,還包括了離線批處理、交互式查詢等業務功能,而且實時計算中,可能還會牽扯到高延遲批處理、交互式查詢等功能,那麼就應該首選Spark生態,用Spark Core開發離線批處理,用Spark SQL開發交互式查詢,用Spark Streaming開發實時計算,三者可以無縫整合,給系統提供非常高的可擴展性。
35.storm核心?
storm被設計成用一種容錯的水平擴展方法處理大量數據。它是一個具有最高攝取率的流式數據框架。雖然Storm是無狀態的,但它通過ApacheZooKeeper管理分佈式環境和集羣狀態。它很簡單,可以對實時數據並行執行各種操作。
ApacheStorm仍然是實時數據分析領域的領導者。Storm易於設置、操作,並確保至少通過TopDlogy處理每個消息(消息至少被消費過一次)。
Storm vs Hadoop
基本上,Hadoop和Storm框架用於分析大數據。兩者相輔相成,在某些方面有所不同。ApacheStorm的所有操作都是持久性的,而Hadoop在所有方面都很好,但在實時計算方面卻落後。

      1. tuple  :元組

      數據結構,有序的元素列表。通常是任意類型的數據,使用,號分割,交給storm計算。

      2. stream  :一系列tuple.

      3. Spouts  :水龍頭。數據源。

      4. Bolts  :轉接頭。邏輯處理單元,spout數據傳給bolt, bolt處理後產生新的數據,可以filter、 聚合、分組。

      接收數據-處理; ->輸出給bolt (多個)。

       5. Topology  :不會停止的,除非手動殺死。MR是會停止的。

       6. tasks  : spout和bolt的執行過程就是task.

      spout和bolt都可以以多實例方式運行(在單獨的線程中)

      7. workers  :工作節點。storm在worker之 間均衡分發任務。監聽job,啓動或者停止進程。

8.stream grouping  :控制tuple如何進行路由。

      內置4個分組策略.

36.sparkStreagming核心抽象?
Dstream
37.什麼是Dstream?
Dstream:sparkstreaming的數據抽象,代表一個持續不斷的數據流。
由一個個連續的batch組成,所謂batch就是一個時間片的rdd。
所以我們在寫sparkstreaming程序的時候,一定設置一個時間間隔,inXXX
首先我們的sparkStreaming程序操作的Dstream ,那麼 地位和spark core 裏面的rdd的地位一樣,它也有轉換和output操作。也會由一個Dstream 經過算子轉變爲另外一個Dstream。
在我們畫的圖中,Dstream01–》Dstream02 ,其實 就是把Dstream裏面的每一個時間段的RDD,都執行了一遍了這個Map,生成了一個新的RDD,新的RDD又組成了Dstream02.
在這裏插入圖片描述
其實Dstream 就是spark 對rdd進行的一個封裝,底層走到流程還是我們之前講的RDD的那一套。
38.sparkStreagming 對象調用stop 方法,參數解釋?
調用stop方法,不僅僅會停止掉streamingContext ,而且可以把sparkContext也會停掉,
如果你想 後續能夠繼續使用 sparkContext ,Stop方法裏面可以寫false
一個JVM裏面只能同時有一個streamingContext ,那麼如果用多個stop(false),再創建一個
39.sparkStreagming 特有算子?

算子 描述
updateStateByKey(func) 返回一個新狀態的DStream,其中每個鍵的狀態是根據鍵的前一個狀態和鍵的新值應用給定函數func後的更新。這個方法可以被用來維持每個鍵的任何狀態數據。
transform(func) 通過對源DStream的每RDD應用RDD-to-RDD函數返回一個新的DStream,這可以用來在DStream做任意RDD操作。
window(windowLength, slideInterval) 返回一個基於源DStream的窗口批次計算後得到新的DStream。

40.sparkStreagming 數據源?
hdfs、flume、kafka,主要是最後一個
41.sparkStreagming receiver?
setMaster(“local[1]”) ,就沒有了打印的結果
因爲注意在spark streaming在默認情況下,接受數據的時候,內部需要有一個叫receiver的東西,它是專門得佔用一個線程來不斷的接受數據。

注意: 創建一個dstream的流,就需要一個receiver ,我們可以針對不同的數據源創建不同的流,可以創建多個流,這些流對應的Dstream當然可以執行我們的算子操作。

小結:我們在設置masterF的時候,這個core 的個數一定要大於等於 流的個數 + 1

我們的sparkStreaming 通過我們的wordCount程序, 得到是一個batch 的數據
42.sparkStreagming 持久化 容錯?
與sparkcore一致
43.程序掛掉分爲?
executor或者driver
44.driver 掛了,如何解決?
這個現象是比executor嚴重的,整個程序只有一個driver。
解決:2種
1.checkpoint,可以對數據進行checkpoint,可以從中恢復出來
2.–supervise,實在spark submit,集羣模式下的參數,不需要加什麼參數
但是如果driver掛掉,會自動重啓
45.可以對那些數據 checkPoint?
元數據,中間結果數據
46.容錯的語義?
至多一個、至少一個、有且只有一個
47.應用程序如何升級?
一種是停掉,然後部署升級。老的停掉,上新的。
現在是在線更新,不停機,老的新的同時計算同一數據,直到兩個計算的數據完全同步,再把老的停掉,自然而然地使用新的
48.sparkStreagming 消費kafka 的數據方式?
1.receiver方式:高階API 採用的是zk保存offset
2.direct方式:低階API,是自己管理偏移量,是可靠的,能保證有且僅有一次的語義
49.receiver方式 如何保證消息的可靠性?
預寫入
50.direct 方式 自己如何保存偏移量?
通過zk ck
51.即席查詢
用戶根據需求 自定義查詢
52.hive 和 spark sql 有什麼區別?
底層不一樣,
hive是mr
spark sql 是spark
53.spark sql特點?
1.易整合
2.統一的數據訪問方式
3.兼容hive
4.標準的數據連接
54.spark1.0與2.0的差別?
spark2.0中sparksession,統一了sqlcontext和hivecontext
55.spark sql的核心抽象?
dataFrame
56.Rdd 和 dataFrame 的區別?
從根本上來說,dataframe具有數據結構,Rdd沒有。
RDD是分佈式的Java對象的集合。DataFrame是分佈式的Row對象的集合。DataFrame除了提供了比RDD更豐富的算子以外,更重要的特點是提升執行效率、減少數據讀取以及執行計劃的優化,比如filter下推、裁剪等。

提升執行效率

RDD API是函數式的,強調不變性,在大部分場景下傾向於創建新對象而不是修改老對象。這一特點雖然帶來了乾淨整潔的API,卻也使得Spark應用程序在運行期傾向於創建大量臨時對象,對GC造成壓力。在現有RDD API的基礎之上,我們固然可以利用mapPartitions方法來重載RDD單個分片內的數據創建方式,用複用可變對象的方式來減小對象分配和GC的開銷,但這犧牲了代碼的可讀性,而且要求開發者對Spark運行時機制有一定的瞭解,門檻較高。另一方面,Spark SQL在框架內部已經在各種可能的情況下儘量重用對象,這樣做雖然在內部會打破了不變性,但在將數據返回給用戶時,還會重新轉爲不可變數據。利用 DataFrame API進行開發,可以免費地享受到這些優化效果。

減少數據讀取

分析大數據,最快的方法就是 ——忽略它。這裏的“忽略”並不是熟視無睹,而是根據查詢條件進行恰當的剪枝。
上文討論分區表時提到的分區剪 枝便是其中一種——當查詢的過濾條件中涉及到分區列時,我們可以根據查詢條件剪掉肯定不包含目標數據的分區目錄,從而減少IO。
對於一些“智能”數據格 式,Spark SQL還可以根據數據文件中附帶的統計信息來進行剪枝。簡單來說,在這類數據格式中,數據是分段保存的,每段數據都帶有最大值、最小值、null值數量等 一些基本的統計信息。當統計信息表名某一數據段肯定不包括符合查詢條件的目標數據時,該數據段就可以直接跳過(例如某整數列a某段的最大值爲100,而查詢條件要求a > 200)。
此外,Spark SQL也可以充分利用RCFile、ORC、Parquet等列式存儲格式的優勢,僅掃描查詢真正涉及的列,忽略其餘列的數據。

執行優化

在這裏插入圖片描述

爲了說明查詢優化,我們來看上圖展示的人口數據分析的示例。圖中構造了兩個DataFrame,將它們join之後又做了一次filter操作。如果原封不動地執行這個執行計劃,最終的執行效率是不高的。因爲join是一個代價較大的操作,也可能會產生一個較大的數據集。如果我們能將filter下推到 join下方,先對DataFrame進行過濾,再join過濾後的較小的結果集,便可以有效縮短執行時間。而Spark SQL的查詢優化器正是這樣做的。簡而言之,邏輯查詢計劃優化就是一個利用基於關係代數的等價變換,將高成本的操作替換爲低成本操作的過程。
得到的優化執行計劃在轉換成物 理執行計劃的過程中,還可以根據具體的數據源的特性將過濾條件下推至數據源內。最右側的物理執行計劃中Filter之所以消失不見,就是因爲溶入了用於執行最終的讀取操作的表掃描節點內。
對於普通開發者而言,查詢優化 器的意義在於,即便是經驗並不豐富的程序員寫出的次優的查詢,也可以被儘量轉換爲高效的形式予以執行。
57.spark on hive與hive on spark: spark的區別
spark on hive: hive作爲數據源使用spark計算
hive on spark: spark作爲計算引擎,使用hive計算
58.Rdd 創建DF 的兩種方式?
籠統的來說,元數據,反射
都是toDF,反射是創建一個樣例類,將Rdd轉換成df
59.開窗函數?

函數 意義
OVER(): 指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變而變化。
CURRENT ROW: 當前行
n PRECEDING: 往前n行數據
n FOLLOWING: 往後n行數據
UNBOUNDED: 起點,UNBOUNDED PRECEDING 表示從前面的起點, UNBOUNDED FOLLOWING表示到後面的終點
LAG(col,n,default_val): 往前第n行數據
LEAD(col,n, default_val): 往後第n行數據
NTILE(n): 把有序分區中的行分發到指定數據的組中,各個組有編號,編號從1開始,對於每一行,NTILE返回此行所屬的組的編號。注意:n必須爲int類型。

60.hive 自定義函數?

hive自定義函數:
(1)UDF(User-Defined-Function)
一進一出
(2)UDAF(User-Defined Aggregation Function)
聚集函數,多進一出
類似於:count/max/min
(3)UDTF(User-Defined Table-Generating Functions)
一進多出
如lateral view explore()
此文章由小白,畢姥爺和趙帥帥三人傾力打造。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章