Spark知识点
一、基本特性
1、spark与MapReduce的不同
① 计算中间结果:mapreduce是基于磁盘维护, 磁盘IO及序列化代价大;而 spark是基于内存的维护 ,基于DAG计算模型,会减少Shaffer过程即磁盘IO减少。
②运行方式式:spark是多线程运行任务,mapreduce是多进程运行任务。进程的启动和关闭和会耗费一定的时间。
③运行模式:mapreduce一般都是on yarn模式;spark可以local部署、standalone 部署以及为on yarn模式
④ 用途:MR只能做离线计算,spark既可以做离线计算,有可以做实时计算,机器学习等
2、spark集群安装部署
- vim spark-env.sh #配置java的环境变量 #配置zk相关信息
- vim slaves 指定spark集群的worker节点
- vim /etc/profile 修改spark环境变量
- 环境变量生效 source /etc/profile
- 启动:
1、先启动zk ${ZK_HOME}/bin/zkServer.sh start
2、启动spark集群 $SPARK_HOME/sbin/start-all.sh
zk作用:高可用
- 在高可用模式下,整个spark集群就有很多个master,其中只有一个master被zk选举成活着的master,其他的多个master都处于standby,同时把整个spark集群的元数据信息通过zk中节点进行保存。
- 如果活着的master挂掉。首先zk会感知到活着的master挂掉,开始在多个处于standby中的master进行选举,再次产生一个活着的master;这个活着的master会读取保存在zk节点中的spark集群元数据信息,恢复到上一次master的状态。
master的恢复阶段,对任务的影响?
- 对已经运行的任务是没有任何影响,由于该任务正在运行,说明它已经拿到了计算资源,这个时候就不需要master。
- 对即将要提交的任务是有影响,由于该任务需要有计算资源,这个时候会找活着的master去申请计算资源,由于没有一个活着的master,该任务是获取不到计算资源,也就是任务无法运行。
3、web管理界面
- http://master主机名:8080
- 集群的详细信息、总资源信息、已用资源信息、还剩资源信息 正在运行的任务信息、已经完成的任务信息
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,然后向它申请计算资源,最后运行程序。
4、spark-shell使用
spark-shell --master local[2]
默认会产生一个SparkSubmit进程
,sc
–master local[N] :表示本地采用N个线程
计算任务
sc.textFile("file:///home/words.txt")
.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_+_).collect
读取HDFS上文件: vim spark-env.sh export HADOOP_CONF_DIR=hdoop安装位置
//实现读取hdfs上文件之后,需要把计算的结果保存到hdfs上
sc.textFile("/words.txt")
.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_+_).saveAsTextFile("/out")
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("E:\\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()
}
}
打成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 jar包与输入输出
spark-submit --class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 1 \
/kkb/install/spark/examples/jars/spark-examples_2.11-2.3.3.jar 10 10是main方法里面的
参数
- executor-memory 小了,会把rdd一部分数据保存在内存中,一部分数据保存在磁盘;用该rdd时从内存和磁盘中获取,一定的磁盘io。需要设置的大一点,如10G/20G/30G等;
- total-executor-cores:表示任务运行需要总的cpu核数,它决定了任务并行运行的粒度,也会设置的大一点,如30个/50个/100个;
加大计算资源它是最直接、最有效果的优化手段。在计算资源有限的情况下,可以考虑其他方面,比如说代码层面,JVM层面等
5、spark on yarn
最大的区别就是Driver端的位置不一样。
-
yarn-cluster: Driver端运行在yarn集群中,与ApplicationMaster进程在一起。
-
yarn-client: Driver端运行在提交任务的客户端,与ApplicationMaster进程没关系,经常 用于进行测试
二、集群架构
1、spark集群架构
(1)Master:主节点,负责任务资源的分配。
(2)Worker:从节点,负责任务计算的节点。
- ①Executor:是一个进程,它会在worker节点启动该进程(计算资源)
- ②Task:任务是以task线程的方式运行在worker节点对应的executor进程中;
(3)ClusterManager:给程序提供计算资源的外部服务,standAlone模式整个任务的资源分配由spark集群的老大Master负责;把spark程序提交到yarn中运行,整个任务的资源分配由yarn中的老大ResourceManager负责
(4)Driver:是所有spark程序的执行入口,会执行客户端写好的main方法,它会构建一个名叫SparkContext对象 ,生成DAG,再根据算子的划分,把任务分到其他节点。
(5)Application:是一个spark的应用程序,它是包含了客户端的代码和任务运行的资源信息
- 一个应用程序application就是提交到spark集群的一个job,会被分成很多个子任务job;
- 其中,一个子任务job中又划分成了许多stage阶段(根据算子间产生shuffe的宽依赖划分);
- 一 个stage中有存在很多分区 ,一个分区就是一个task,即一个stage中有很多个task;
- 一个action操作对应一个DAG有向无环图,即一个action操作就是一个job;
2、调度分配
Spark中的调度模式:FIFO(先进先出)、FAIR(公平调度)
任务的分配资源worker策略:尽量打散、尽量集中
尽量打散:一个Application尽可能多的分配到不同的节点,发挥数据的本地性,提升执行效率
尽量集中:尽量分配到尽可能少的节点
3、运行模式
(1) local 本地模式
该模式主要用作测试用,一般编写的 spark 程序,将 master 设置为 local 或者local[n],以本地模式运行,所有的代码都在一个 Jvm 里面。
(2) Standalone 模式
该模式由 Spark 自带的集群管理模式,不依赖外部的资源管理器,由 Master 负责资源的分配管理,Worker 负责运行 Executor ,具体的运行过程可参考之前介绍 Spark 运行模式的篇章。
(4) yarn 模式
该模式由 yarn 负责管理整个集群资源,不再有 Master 和 Worker,根据 yarn-client 和 yarn-cluster 的不同。
yarn-client 中 driver运行在本地客户端,负责调度application,会与yarn集群产生大量的网络通信,但本地可以看见日志。
yarn-cluster 中 driver运行在yarn集群中,看不见日志。
(5) Mesos 模式
和 yarn 一样,Mesos 中,Spark 的资源管理从 Standalone 的 Master 转移到 Mesos Manager
中。
4、运行流程
spark集群模式,
-
先开始资源准备:
- 1)diver 拿到代码包,先到master那去,注册和申请计算资源;
- 2)master知道后,就通知woker启动好executor进程开始准备一下;
- 3)woker准备好进程后,就通知Driver自己好了,发送注册并且申请task请求;
-
接下来,diver就运行main方法,分任务,运行程序;
- 1)diver 先构建SparkContext对象,在SparkContext对象内部依次构建DAGScheduler和TaskScheduler;
- 2)diver再根据代码rdd算子的操作顺序,生成DAG有向无环图;
- 3)DAGScheduler拿到DAG后,按照宽依赖进行stage划分,每一个stage内部有很多可以并行运行的task,最后封装在一个一个的taskSet集合中,并把taskSet发送给TaskScheduler;
- 4)TaskScheduler拿到taskSet集合后,依次遍历取出每一个task,提交task到worker节点上的executor进程中运行;
-
最后,程序运行完,diver通知Master注销,Master通知Worker关闭executor进程 ;
5、SparkContext对象内部
1.创建SparkEnv,里面有一个很重要的对象ActorSystem。
- 在SparkContext中创建了两个Actor,
- 一个是DriverActor,这里主要用于Driver和Executor之间的通信;
- 还有一个是ClientActor,主要用于Driver和Master之间的通信。
2.创建TaskScheduler,这里是根据提交的集群来创建相应的TaskScheduler
- 对于TaskScheduler,主要的任务调度模式有FIFO和FAIR
- 调用taskScheduler.start()方法启动,进行资源调度,有两种资源分配方法,一种是尽量打散;一种是尽量集中
3.创建DAGScheduler,用于Stage的划分
- Driver向Master注册,发送了一些信息,其中一个重要的类是CoarseGrainedExecutorBackend,这个类以后用于创建Executor进程。
三、计算资源
1、RDD概念
RDD(Resilient Distributed Dataset)叫做弹性 分布式 数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合.
- Resilient: 表示弹性,rdd的数据是可以保存在内存或者是磁盘中.
- Distributed:它内部的元素进行了分布式存储,方便于后期进行分布式计算.
- Dataset: 就是一个集合,存储很多数据.
五大属性:
- 1)A list of partitions:一个rdd有很多分区,每一个分区内部是包含了该rdd的部分数据
- 2)A function for computing each split:每个分区都会实现 计算函数
- 3)A list of dependencies on other RDDs:一个rdd会依赖于其他多个rdd
- 4)Optionally, a Partitioner for key-value RDDs :kv数据的分区函数基于哈希,非kv是None
- 5)Optionally, a list of preferred locations to compute each split on:有分区数据的节点会优先开启计算任务,数据的本地性。
RDD自定义分区
RDD数据进行分区时,默认使用的是HashPartitioner:对key进行哈希,然后对分区总数取模,
实现自定义partitioner大致分为3个步骤
- 继承org.apache.spark.Partitioner
- 重写numPartitions方法
- 重写getPartition方法
//5、对应上面的rdd数据进行自定义分区
val result: RDD[(String, Int)] = wordLengthRDD.partitionBy(new MyPartitioner(3))
2、RDD的创建
1、scala集合sc.parallelize
val rdd1=sc.parallelize(List(1,2,3,4,5))
val rdd2=sc.parallelize(Array("hadoop","hive","spark"))
val rdd3=sc.makeRDD(List(1,2,3,4))
2、加载外部的数据源sc.textFile
val rdd1=sc.textFile("/words.txt")
3、已存在rdd转换成一个新的