01_Spark_基础

什么是Spark?

Spark是基于内存计算大数据分析引擎,提高了在大数据环境下数据处理的实时性。Spark仅仅只涉及到数据的计算,没有涉及到数据的存储。

Spark的特点及相对于MapReduce的优势

MapReduce存在的问题

  1. MapReduce框架局限性
      1)仅支持Map和Reduce两种操作
      2)处理效率低效。
        a)Map中间结果写磁盘,Reduce写HDFS,多个MR之间通过HDFS交换数据; 任务调度和启动开销大;
        b)无法充分利用内存
        c)Map端和Reduce端均需要排序
      3)不适合迭代计算(如机器学习、图计算等),交互式处理(数据挖掘) 和流式处理(点击日志分析)
  2. MapReduce编程不够灵活
      1)尝试scala函数式编程语言
    Spark的特点及优势
  3. 高效(比MapReduce快10~100倍)
      1)内存计算引擎,提供Cache机制来支持需要反复迭代计算或者多次数据共享,减少数据读取的IO开销
      2)DAG引擎,减少多次计算之间中间结果写到HDFS的开销
      3)使用多线程池模型来减少task启动开稍,shuffle过程中避免 不必要的sort操作以及减少磁盘IO操作
  4. 易用
      1)提供了丰富的API,支持Java,Scala,Python和R四种语言
      2)代码量比MapReduce少2~5倍
    3.兼容性
    可与Hadoop集成 读写HDFS/Hbase/Cassandra 与YARN集成
    4.通用性
    Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)
Spark HA高可用部署
Spark HA解决Master单点故障的两种方案:

1.基于文件系统的单点恢复(主要用于开发或测试环境)
2.基于zookeeper的Standby Masters(用于生产模式)

*基于zookeeper的Spark HA高可用集群部署
(1)vim spark-env.sh 注释掉export SPARK_MASTER_HOST=hdp-node-01
(2)在spark-env.sh添加SPARK_DAEMON_JAVA_OPTS,内容如下:

export SPARK_DAEMON_JAVA_OPTS="-
Dspark.deploy.recoveryMode=ZOOKEEPER  -
Dspark.deploy.zookeeper.url=hdp-node-01:2181,hdp-node-02:2181,hdp-node-
03:2181  -Dspark.deploy.zookeeper.dir=/spark"

参数说明
spark.deploy.recoveryMode:
恢复模式(Master重新启动的模式)有三种:
(1)ZooKeeper (2) FileSystem (3)NONE
spark.deploy.zookeeper.url:ZooKeeper的Server地址
spark.deploy.zookeeper.dir:保存集群元数据信息的文件、目录。
包括Worker,Driver和Application。
注意
在普通模式下启动spark集群,只需要在主机上面执行start-all.sh 就可以了。
在高可用模式下启动spark集群,先需要在任意一台节点上启动start-all.sh命令。然后在另外一台节点上单独启动master。命令start-master.sh。

Spark的角色

image.png

运⾏main函数并且新建SparkContext的程序(初始化工作)。
基于Spark的应用程序,包含了driver程序和集群上的executor(代码逻辑与运行资源)。
指的是在集群上获取资源的外部服务(提供外部运行资源,资源分配)。目前有三种类型
(1)Standalone: spark原生的资源管理,由Master负责资源的分配
(2)Apache Mesos:与hadoop MR兼容性良好的一种资源调度框架
(3)Hadoop Yarn: 主要是指Yarn中的ResourceManager
集群中老大,负责资源的分配和任务的调度

集群中任何可以运行Application代码的节点(小弟),在Standalone模式中指的是通过slaves文件配置的Worker节点,在Spark on Yarn 模式下就是NodeManager节点

是在一个worker node上为某应⽤启动的⼀个进程,该进程负责运⾏行任务,并且负责将数据存在内存或者磁盘上。每个应⽤都有各自独立的executor(运行当前任务所需要的资源)。
被送到某个executor上的工作单元(线程)。
Spark中设置的数据缓存

Spark程序提交任务模式

普通模式提交任务(Master节点已知):

bin/spark-submit \(提交任务脚本)
--class org.apache.spark.examples.SparkPi \(需要运行的主类)
--master spark://hdp-node-01:7077 \(指定任务提交到哪个Master)
--executor-memory 1G \(指定executor内存大小)
--total-executor-cores 2 \(总executor运行核数)
examples/jars/spark-examples_2.11-2.0.2.jar \(程序jar包所在地址)
10(main方法所需参数)

高可用模式提交任务:

bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://hdp-node-01:7077,hdp-node-02:7077,hdp-node-03:7077 \
(指定Master列表)
--executor-memory 1G \
--total-executor-cores 2 \
examples/jars/spark-examples_2.11-2.0.2.jar \
10

Spark-Shell

读取本地文件

1.运行spark-shell --master local[N](N表线程数)
2.编写scala代码
sc.textFile("file:///root///words.txt")
.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect

读取HDFS上数据

1.整合spark和HDFS,修改配置文件spark-env.sh
 export HADOOP_CONF_DIR=/opt/bigdata/hadoop-2.6.4/etc/hadoop
2.启动hdfs,然后重启spark集群
3. 向hdfs上传个文件
4.在spark shell中用scala语言编写spark程序

指定具体的master地址

1.执行启动命令:
       spark-shell \
--master spark://hdp-node-01:7077 \
--executor-memory 1g \
--total-executor-cores 2
      若没指定master地址则默认本地模式
2.编写scala代码

在IDEA中编写WordCount程序

1.创建Maven项目并配置pom.xml
2.添加src/main/scala和src/test/scala,与pom.xml中的配置保持一致
3.新建一个scala class,类型为Object,并编写spark程序
4.使用Maven打包上传到Spark集群中的某个节点上
5.启动hdfs和Spark集群
6.使用spark-submit命令提交Spark应用




简述Spark的架构与作业提交流程(画图讲解,注明各个部分的作用)(重点)

image.png
Spark运行基本流程

Spark运行基本流程参见下面示意图:

image.png

1.构建Spark Application的运行环境(启动SparkContext),SparkContext向资源管理器(可以是Standalone、Mesos或YARN)注册并申请运行Executor资源;
2.资源管理器分配Executor资源并启动Executor,Executor运行情况将随着心跳发送到资源管理器上;
3.SparkContext构建成DAG图,将DAG图分解成Stage,并把Taskset发送给Task Scheduler。Executor向SparkContext申请Task,Task Scheduler将Task发放给Executor运行同时SparkContext将应用程序代码发放给Executor。
4.Task在Executor上运行,运行完毕释放所有资源。
◆每个Application获取专属的executor进程,该进程在Application期间一直驻留,并以多线程方式运行tasks。
◆Spark任务与资源管理器无关,只要能够获取executor进程,并能保持相互通信就可以了。
◆提交SparkContext的Client应该靠近Worker节点(运行Executor的节点),最好是在同一个Rack里,因为Spark程序运行过程中SparkContext和Executor之间有大量的信息交换;如果想在远程集群中运行,最好使用RPC将SparkContext提交给集群,不要远离Worker运行SparkContext。
◆Task采用了数据本地性和推测执行的优化机制。
应用内调度
在指定的 Spark 应用内部(对应同一 SparkContext 实例),多个线程可能并发地提交 Spark 作业(job)。在本节中,作业(job)是指,由 Spark action 算子(如 : collect)触发的一系列计算任务的集合。Spark 调度器是完全线程安全的,并且能够支持 Spark 应用同时处理多个请求(比如 : 来自不同用户的查询)。
默认,Spark 应用内部使用 FIFO 调度策略。每个作业被划分为多个阶段(stage)(例如 : map 阶段和 reduce 阶段),第一个作业在其启动后会优先获取所有的可用资源,然后是第二个作业再申请,再第三个……。如果前面的作业没有把集群资源占满,则后续的作业可以立即启动运行,否则,后提交的作业会有明显的延迟等待。
不过从 Spark 0.8 开始,Spark 也能支持各个作业间的公平(Fair)调度。公平调度时,Spark 以轮询的方式给每个作业分配资源,因此所有的作业获得的资源大体上是平均分配。这意味着,即使有大作业在运行,小的作业再提交也能立即获得计算资源而不是等待前面的作业结束,大大减少了延迟时间。这种模式特别适合于多用户配置。 要启用公平调度器,只需设置一下 SparkContext 中

spark.scheduler.mode 属性为 FAIR 即可 :
val conf = new SparkConf().setMaster(...).setAppName(...)
conf.set("spark.scheduler.mode", "FAIR")
val sc = new SparkContext(conf)

公平调度资源池

公平调度器还可以支持将作业分组放入资源池(pool),然后给每个资源池配置不同的选项(如: 权重)。这样你就可以给一些比较重要的作业创建一个“高优先级”资源池,或者你也可以把每个用户的作业分到一组,这样一来就是各个用户平均分享集群资源,而不是各个作业平分集群资源。Spark 公平调度的实现方式基本都是模仿 [Hadoop Fair Scheduler]

(http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html). 

来实现的。
默认情况下,新提交的作业都会进入到默认资源池中,不过作业对应于哪个资源池,可以在提交作业的线程中用 SparkContext.setLocalProperty 设定 spark.scheduler.pool 属性。示例代码如下 :

// Assuming sc is your SparkContext variable
sc.setLocalProperty("spark.scheduler.pool", "pool1")

一旦设好了局部属性,所有该线程所提交的作业(即 : 在该线程中调用action算子,如 : RDD.save/count/collect 等)都会使用这个资源池。这个设置是以线程为单位保存的,你很容易实现用同一线程来提交同一用户的所有作业到同一个资源池中。同样,如果需要清除资源池设置,只需在对应线程中调用如下代码 :

sc.setLocalProperty("spark.scheduler.pool", null)

资源池默认行为

默认地,各个资源池之间平分整个集群的资源(包括 default 资源池),但在资源池内部,默认情况下,作业是 FIFO 顺序执行的。举例来说,如果你为每个用户创建了一个资源池,那么久意味着各个用户之间共享整个集群的资源,但每个用户自己提交的作业是按顺序执行的,而不会出现后提交的作业抢占前面作业的资源。

配置资源池属性
资源池的属性需要通过配置文件来指定。每个资源池都支持以下3个属性 :
• schedulingMode: 可以是 FIFO 或 FAIR,控制资源池内部的作业是如何调度的。
• weight: 控制资源池相对其他资源池,可以分配到资源的比例。默认所有资源池的 weight 都是 1。如果你将某个资源池的 weight 设为 2,那么该资源池中的资源将是其他池子的2倍。如果将 weight 设得很高,如 1000,可以实现资源池之间的调度优先级 – 也就是说,weight=1000 的资源池总能立即启动其对应的作业。
• minShare: 除了整体 weight 之外,每个资源池还能指定一个最小资源分配值(CPU 个数),管理员可能会需要这个设置。公平调度器总是会尝试优先满足所有活跃(active)资源池的最小资源分配值,然后再根据各个池子的 weight 来分配剩下的资源。因此,minShare 属性能够确保每个资源池都能至少获得一定量的集群资源。minShare 的默认值是 0。
资源池属性是一个 XML 文件,可以基于 conf/fairscheduler.xml.template 修改,然后在 SparkConf. 的 spark.scheduler.allocation.file 属性指定文件路径:
conf.set("spark.scheduler.allocation.file", "/path/to/file")

image.png

简述Spark的宽窄依赖,以及Spark如何划分stage,每个stage又根据什么决定task个数? (笔试重点)

Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。
Task:Stage是一个TaskSet,将Stage根据分区数划分成一个个的Task。

简述Spark的两种核心Shuffle(HashShuffle与SortShuffle)的工作流程(包括未优化的HashShuffle、优化的HashShuffle、普通的SortShuffle与bypass的SortShuffle)(重点)

未经优化的HashShuffle:

HashShuffle流程

image.png

优化后的Shuffle流程:

image.png

当 shuffle read task 的 数 量 小 于 等 于 spark.shuffle.sort。
bypassMergeThreshold 参数的值时(默认为 200),就会启用 bypass 机制。


image.png

Repartition和Coalesce关系与区别

1)关系:
两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:coalesce(numPartitions, shuffle = true)
2)区别:
repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生shuffle
一般情况下增大rdd的partition数量使用repartition,减少partition数量时使用coalesce

请手写出wordcount的Spark代码实现(Scala)(手写代码重点)

 val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
 val sc = new SparkContext(conf)
 sc.textFile("/input")
.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_+_)
.saveAsTextFile("/output")
 sc.stop()

如何使用Spark实现topN的获取(描述思路或使用伪代码)(重点)

方法1:
(1)按照key对数据进行聚合(groupByKey)
(2)将value转换为数组,利用scala的sortBy或者sortWith进行排序(mapValues)数据量太大,会OOM。
方法2:
(1)取出所有的key
(2)对key进行迭代,每次取出一个key利用spark的排序算子进行排序
方法3:
(1)自定义分区器,按照key进行分区,使不同的key进到不同的分区
(2)对每个分区运用spark的排序算子进行排序

某大厂调优例子:调优之前与调优之后性能的详细对比(例如调整map个数,map个数之前多少、之后多少,有什么提升)

这里举个例子。比如我们有几百个文件,会有几百个map出现,读取之后进行join操作,会非常的慢。这个时候我们可以进行coalesce操作,比如240个map,我们合成60个map,也就是窄依赖。这样再shuffle,过程产生的文件数会大大减少。提高join的时间性能。

当Spark涉及到数据库的操作时,如何减少Spark运行中的数据库连接数?

使用foreachPartition代替foreach,在foreachPartition内获取数据库的连接。

简述Spark中共享变量(广播变量和累加器)的基本原理与用途。(重点)

参考:Broadcast广播变量和accumulator累加器
累加器(accumulator)是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。而广播变量用来高效分发较大的对象。
共享变量出现的原因:
通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。
Spark的两个共享变量,累加器与广播变量,分别为结果聚合与广播这两种常见的通信模式突破了这一限制。

分别简述Spark中的缓存机制(cache和persist)与checkpoint机制,并指出两者的区别与联系

都是做RDD持久化的
cache:内存,不会截断血缘关系,使用计算过程中的数据缓存。
checkpoint:磁盘,截断血缘关系,在ck之前必须没有任何任务提交才会生效,ck过程会额外提交一次任务。




Spark_算子

Transformations

image.png

image.png

image.png

OutputOperations

Output Operations可以将DStream的数据输出到外部的数据库或文件系统,

Output Operation Meaning
print() 打印到控制台
saveAsTextFiles(prefix, [suffix]) 保存流的内容为文本文件,文件名为"prefix-TIME_IN_MS[.suffix]".
saveAsObjectFiles(prefix, [suffix]) 保存流的内容为SequenceFile,文件名为"prefix-TIME_IN_MS[.suffix]".
saveAsHadoopFiles(prefix, [suffix]) 保存流的内容为hadoop文件,文件名为"prefix-TIME_IN_MS[.suffix]".
foreachRDD(func) 对Dstream里面的每个RDD执行func
请列举Spark的transformation算子(不少于8个),并简述功能(重点)

1)map(func):返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成.
2)mapPartitions(func):类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
3)reduceByKey(func,[numTask]):在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
4)aggregateByKey (zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U: 在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
5)combineByKey(createCombiner: V=>C, mergeValue: (C, V) =>C, mergeCombiners: (C, C) =>C):
对相同K,把V合并成一个集合。
1.createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
2.mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
3.mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。

根据自身情况选择比较熟悉的算子加以介绍。

请列举Spark的action算子(不少于6个),并简述功能(重点)

1)reduce:
2)collect:
3)first:
4)take:
5)aggregate:
6)countByKey:
7)foreach:
8)saveAsTextFile:
4.10.9 请列举会引起Shuffle过程的Spark算子,并简述功能。
reduceBykey:
groupByKey:
…ByKey:

Spark常用算子reduceByKey与groupByKey的区别,哪一种更具优势?(重点)

reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]。
groupByKey:按照key进行分组,直接进行shuffle。
开发指导:reduceByKey比groupByKey,建议使用。但是需要注意是否会影响业务逻辑。




配置资源池属性

资源池的属性需要通过配置文件来指定。每个资源池都支持以下3个属性 :

• schedulingMode: 可以是 FIFO 或 FAIR,控制资源池内部的作业是如何调度的。

• weight: 控制资源池相对其他资源池,可以分配到资源的比例。默认所有资源池的 weight 都是 1。如果你将某个资源池的 weight 设为 2,那么该资源池中的资源将是其他池子的2倍。如果将 weight 设得很高,如 1000,可以实现资源池之间的调度优先级 – 也就是说,weight=1000 的资源池总能立即启动其对应的作业。

• minShare: 除了整体 weight 之外,每个资源池还能指定一个最小资源分配值(CPU 个数),管理员可能会需要这个设置。公平调度器总是会尝试优先满足所有活跃(active)资源池的最小资源分配值,然后再根据各个池子的 weight 来分配剩下的资源。因此,minShare 属性能够确保每个资源池都能至少获得一定量的集群资源。minShare 的默认值是 0。

资源池属性是一个 XML 文件,可以基于 conf/fairscheduler.xml.template 修改,然后在 SparkConf. 的 spark.scheduler.allocation.file 属性指定文件路径:

conf.set("spark.scheduler.allocation.file", "/path/to/file")

你可能感兴趣的:(01_Spark_基础)