什么是spark 

定义:spark是一种基于内存快速、通用、可扩展的大数据分析引擎。

spark内置模块

底层的调度器区分三种:基于独立调度器,yarn,mesos

中间层:spark core 

提供几种类:spark sql ,spark streaming 实时计算,spark mlib 机器学习,spark graghx图计算

spark core实现了spark 的基本功能,包括任务调度,内存管理,错误恢复,与存储系统等模块。spark core还包含了对弹性分布式数据集(resilient distributed datase)简称rdd的API定义。

spark sql:是spark 用来操作结构化数据的程序包,通过spark sql,我们可以使用sql 或apache hive 版本Sql方言hsql来查询数据。spark sql 支持多种数据源,比如hive表,parquest以json等。

spark streaming:是spark 提供的实时数据进行流式计算的组件,提供了用来操作数据流的api,并且与spark core中的rdd api高度对应。

spark mlib:提供常见的机器学习(ML)功能的程序库,包括:分类,回归,聚类,协同过滤等。还提供了模型评估。数据导入等额外 的支持功能。

集群管理器:spark 设计为可惟高效地在一个计算节点到数千个计算节点之间伸缩计算。为了实现这样的要求,同时获得最大的灵活性,spark 支持在各种集群管理器(cluster manager)上运行,包括:hadoop yarn ,apache mesos,以及spark 自带的简易调度器,叫作独立调度器。

百度的spark 已应用于大搜索,直达号、百度大数据等业务。阿里利用grapx 构建了大规模的图计算和图挖掘系统,实现很多生产系统的推荐系统。

spark特点:

快,易用,通用,兼容性

spark 的运行模式

下载地址:https://spark.apache.org/downloads.html

重要角色:

driver(驱动器)

spark 驱动器是执行开发过程中的main方法的进程。它负责开发人员编写用来创建spark context、创建rdd、以及进行rdd的转化操作和行动操作。如果你是用spark shell,那么当你启动Spark shell的时候,系统后台自启一个spark 驱动程序 ,就是在spark shell 中预加载一个叫作sc的sparkcontext对象,

1)把用户程序转为作业(JOB)

2)跟踪Executor的运行状况

3)为执行器节点调度任务

4)UI展示应用运行状况

执行器(Executor)

spark executor是一个工作进程,负责在spark 作业中运行任务,任务间相到独立。

 bin/spark-submit --class org.apache.spark.examples.SparkPi  --master yarn   --deploy-mode  cluster    --executor-memory 1G --total-executor-cores 2 examples/jars/spark-examples_2.11-2.3.0.2.6.5.0-292.jar  

Local模式

local模式就是运行在一台计算机上的模式,通过就是用于本机上练手和测试

local所有计算都运行在一个线程当中,没任何并行计算,通常我们在本地上执行一些测试代码,或者练手,就用这种模式。

local[k]指定向个线程来运行计算,比如local[4]就是运行4个worker线程,通常我们的cpu是几个,就是几个线程,最大化利用cpu的计算能力。

local[*]这种模式直接帮助cpu最多cores来设置线程 数。

wordcount = sc.textFile("file:///home/wjjfileinput").flatMap(_.split("\\W+")).map((_,1)).reduceByKey(_+_).collect()

 sc.textFile("file:///home/wjjfileinput").flatMap(_.split("\\W+")).map((_,1)).reduceByKey(_+_).collect()

数据流分析:

textFile("input"):读取本地文件input文件夹数据

flatMap(_.split("\\W+")):压平操作,按照空格分割符将一行数据映射成一个个单词

map((_,1)) 对每一个元素进行操作,将单词映射成元组。

reduceByKey(_+_)按照key将值进行聚合,相加

collect将数据收集到driver端展示

standalone模式:

构建一个由master+slave构成的,spark运行的集群

按照时需要

修改conf下面的

mv slaves.templete slaves 

mv spark-env.sh.templete spark-env.sh 

添加如下配置

SPARK_MASTER_HOST=hadoop102

SPARK_MASTER_PORT=7077

集群都需要同步修改配置

sbin/start-all.sh 

如果遇到报java_home not set异常,需要在sbin止录下的spark-config.sh 文件中加入如下配置

export JAVA_HOME=XXXX

bin/spark-submit \

--class org.apache.spark.examples.SparkPi \

--master spark://hadoop102:7077 \

--executor-memory 1G \

--total-executor-cores 2 \

./examples/jars/spark-examples_2.11-2.1.1.jar \

100

 JobHistoryServer配置

1)修改spark-default.conf.template名称

[atguigu@hadoop102 conf]$ mv spark-defaults.conf.template spark-defaults.conf

2)修改spark-default.conf文件,开启Log:

[atguigu@hadoop102 conf]$ vi spark-defaults.conf

spark.eventLog.enabled           true

spark.eventLog.dir               hdfs://hadoop102:9000/directory

注意:HDFS上的目录需要提前存在。

[atguigu@hadoop102 hadoop]$ hadoop fs –mkdir /directory

3)修改spark-env.sh文件,添加如下配置:

[atguigu@hadoop102 conf]$ vi spark-env.sh

 

export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=18080

-Dspark.history.retainedApplications=30

-Dspark.history.fs.logDirectory=hdfs://hadoop102:9000/directory"

参数描述:

spark.eventLog.dir:Application在运行过程中所有的信息均记录在该属性指定的路径下;

spark.history.ui.port=18080  WEBUI访问的端口号为18080

park.history.fs.logDirectory=hdfs://hadoop102:9000/directory  配置了该属性后,在start-history-server.sh时就无需再显式的指定路径,Spark History Server页面只展示该指定路径下的信息

spark.history.retainedApplications=30指定保存Application历史记录的个数,如果超过这个值,旧的应用程序信息将被删除,这个是内存中的应用数,而不是页面上显示的应用数。

HA配置

1)zookeeper正常安装并启动

2)修改spark-env.sh文件添加如下配置:

[atguigu@hadoop102 conf]$ vi spark-env.sh

 

注释掉如下内容:

#SPARK_MASTER_HOST=hadoop102

#SPARK_MASTER_PORT=7077

添加上如下内容:

export SPARK_DAEMON_JAVA_OPTS="

-Dspark.deploy.recoveryMode=ZOOKEEPER

-Dspark.deploy.zookeeper.url=hadoop102,hadoop103,hadoop104

-Dspark.deploy.zookeeper.dir=/spark"

3)分发配置文件

[atguigu@hadoop102 conf]$ xsync spark-env.sh

4)在hadoop102上启动全部节点

[atguigu@hadoop102 spark]$ sbin/start-all.sh

5)在hadoop103上单独启动master节点

[atguigu@hadoop103 spark]$ sbin/start-master.sh

6)spark HA集群访问

/opt/module/spark/bin/spark-shell \

--master spark://hadoop102:7077,hadoop103:7077 \

--executor-memory 2g \

--total-executor-cores 2

Yarn模式

spark 客户端直接连接yarn ,不需要额外spark集群,有yarn-client和yarn-cluster 

两种模式

yarn-client:Driver程序运行在客户端,适用于交互、调试,希望立即看到app的输出

yarn-cluster:Driver程序运行在由RM(ResourceManager)启动的AP(APPMaster)适用于生产环境。

修改hadoop配置文件yarn-site.xml,添加如下内容:

 vi yarn-site.xml
        yarn.nodemanager.pmem-check-enabledfalseyarn.nodemanager.vmem-check-enabledfalse

)修改spark-env.sh,添加如下配置:

[atguigu@hadoop102 conf]$ vi spark-env.sh

 

YARN_CONF_DIR=/opt/module/hadoop-2.7.2/etc/hadoop

3)分发配置文件

[atguigu@hadoop102 conf]$ xsync /opt/module/hadoop-2.7.2/etc/hadoop/yarn-site.xml

[atguigu@hadoop102 conf]$ xsync spark-env.sh

4)执行一个程序

[atguigu@hadoop102 spark]$ bin/spark-submit \

--class org.apache.spark.examples.SparkPi \

--master yarn \

--deploy-mode client \

./examples/jars/spark-examples_2.11-2.1.1.jar \

100

打包方式:

4.0.0com.wangjunjiSparkEvCheck1.0-SNAPSHOTUTF-8org.apache.sparkspark-core_2.112.1.1org.tachyonprojecttachyon-clientorg.apache.curatorcurator-recipescommons-codeccommons-codecorg.scala-toolsmaven-scala-plugin2.15.2compiletestCompilemaven-compiler-plugin3.6.01.81.8org.apache.maven.pluginsmaven-assembly-plugin2.3jar-with-dependenciesorg.apache.maven.pluginsmaven-surefire-plugin2.19trueorg.apache.maven.pluginsmaven-shade-plugin2.4.3packageshade*:*META-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSAcompile

mvn clean scala:compile compile package

本地调试

本地Spark程序调试需要使用local提交模式,即将本机当做运行环境,Master和Worker都为本机。运行时直接加断点调试即可。如下:

创建SparkConf的时候设置额外属性,表明本地执行:

val conf = new SparkConf().setAppName("WC").setMaster("local[*]")

    如果本机操作系统是windows,如果在程序中使用了hadoop相关的东西,比如写入文件到HDFS,则会遇到如下异常:

 

出现这个问题的原因,并不是程序的错误,而是用到了hadoop相关的服务,解决办法是将附加里面的hadoop-common-bin-2.7.3-x64.zip解压到任意目录。

 

在IDEA中配置Run Configuration,添加HADOOP_HOME变量

 

RDD的依赖关系

Lineage(血缘)

RDD只支持粗粒度的转换,即在大量的记录上执行单个操作。将转建RDD的一系列Lineage记录下来,以便恢复丢 失的分区,RDD的血缘会记录RDD的元数据信息和转换行为。

// 一个正常
val wordAndOne = sc.textFile("/passwd").flatMap(_.split(":")).map((_,1)).reduceByKey(_+_)
//wordAndOne 的血缘检查
wordAndOne.toDebugString //查看血缘依赖
//查看血缘的依赖
 wordAndOne.dependencies

注意:RDD和它的依赖父RDD的关系有两种不同类型,即窄依赖和宽依赖

窄依赖,指的是每个父rdd的分区最多被子rdd的一个partition使用,窄依赖我们形象的比喻为独生子女

宽依赖:宽依赖是多个子rdd的分区parttion会依赖一个父rdd的parttions会依赖两只一个父的parttion早多被子rdd的一个partition使用,会引起Shuffle

总结,宽依赖我们形象的比喻为超生。

 

DAG:DAG叫做有向无环图,原始的RDD通过一系列的转换就形成了DAG,根据rdd之间的依赖关系不同将dag划分成不同的stage,对于窄依赖,partition的转换处理在stage中完成计算,对于宽依赖,由于有shuffle存在,只能在partition处理完成后,才开始接下来的计算。因此宽依赖是划分stage的依据。

 

任务划分

rdd任务切分中间分为 application , job ,stage 和task

Application:初始化一个sparkcontext即生成一个application 

job,一个action算子就会生成一个job

stage:根据rdd之间的依赖关系的不同将job划分成不同的Stage,遇到一个宽依赖则划分一个stage 

task:stage 是一个taskset,将stage划分的结果发送到不同的executor执行即为一个task 

applciton -->job -->stage--->task 每一层都是1对n的关系。

RDD缓存

rdd通过persist方法或者cache方法可以将前面的计算结果缓存,默认情况下,persist会把数据以序列化的形式缓存到jvm的堆空间中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该rdd将会被缓存到计算节点内存中,并供后面使用。

调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。

缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除,rdd的缓存容错机制保证了即使用缓存丢失也能保证计算的正确丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。

创建一个rdd

val rdd = sc.makeRDD(Array("atguigu"))
val nocache = rdd.map(_.toString+System.currentTimeMillis)
nocache.collect
val cache =  rdd.map(_.toString+System.currentTimeMillis).cache
多次打印结果都是一样的

RDD CheckPoint

spark 中对于数据的保存除了持久化操作之外,还提供一种检查点的机制,检查点(本质是通过将rdd写入disk做检查点)是为了通过lineage做容错的辅助,leingage过长会造 成容错成本过高,这样就不如在中间阶段做检查点容错,如果之后有节点出现问题,而丢 失分区,从检查点rdd开始lineage,就会减少开销,检查点通过将数据写入到hdfs文件系统实现了rdd检查点功能。

为当前rdd设置检查点,该函数将会创建一个二进制文件,并存储到checkpoint目录中,该目录 是用sparkcontext.setcheckpointdir设置,在check point过程中,该 rdd的所有依赖于父rdd中的信息将全部被移除,对rdd进行checkpoint操作并不会马上被执行,必须执行action操作才能解发。

RDD中的函数传递

在实际开发中我们往往需要自已定义一些对于rdd的操作,那么此时需要主要的是,初始化工作dirver端进行的,而实于程序是在Executor端进行的,这就涉及到了跨进程通信,需要序列化,

//持久化操作
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object SeriTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("serivcount").setMaster("local[*]")
    val sc = new SparkContext(conf)
    var rddinfo: RDD[String] = sc.parallelize(Array("hadoop", "spark", "hive","java", "test1244"))
    val search = new Search()
    var unit: RDD[String] = search.getMatch1(rddinfo)
    unit.foreach(println)
  }

}

// 将driver里面的方法传递到excutor需要序列化
class Search( ) extends Serializable {
  //过滤包含字符串的数据
  def isMatch(s:String):Boolean={
    s.contains("java");
  }

  def getMatch1(rdd:RDD[String]):RDD[String]={
    rdd.filter(isMatch)
  }



  //过滤出包含字符串的RDD
  def getMatche2(rdd: RDD[String]): RDD[String] = {
    rdd.filter(x => x.contains("java"))
  }

}

如果没有持久化将报错

	at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:108)
	at org.apache.spark.SparkContext.clean(SparkContext.scala:2101)
	at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:387)
	at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:386)
	at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)
	at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:112)
	at org.apache.spark.rdd.RDD.withScope(RDD.scala:362)
	at org.apache.spark.rdd.RDD.filter(RDD.scala:386)
	at com.wangjunji.rddoperation.Search.getMatch1(Search.scala:12)
	at com.wangjunji.rddoperation.SeriTest$.main(SeriTest.scala:12)
	at com.wangjunji.rddoperation.SeriTest.main(SeriTest.scala)
Caused by: java.io.NotSerializableException: com.wangjunji.rddoperation.Search
Serialization stack:
	- object not serializable (class: com.wangjunji.rddoperation.Search, value: com.wangjunji.rddoperation.Search@19f21b6b)

类的局部变量,需要重新给值

ACtion操作

reduce(func)案例

作用:通过func函数聚集RDD中所有元素,先聚合分区内数据,再聚合分区间的数据

需求:创建一个rdd,将所有元素聚合得到结果

 

键值对rdd数据分区器:

spark目前支持hash分区和range分区,用户也可以自定义分区,hash分区为当前的默认分区,