Spark工作原理及相关内核架构

Spark基本工作原理

Spark于Mapreduce的最大不同在于Spark是迭代式计算模型:

  • Map Reduce分为两个阶段,map和reduce。两个阶段完了,就结束了,所以我们在一个job里能做的处理很有限,只能在map和reduce里处理。
  • Spark计算模型,可以分为n个阶段,因为他是内存迭代式的。我们在处理完一个阶段以后,可以继续往下处理很多个阶段,所以Spark相较于MapReduce可以提供更强大的功能。
    在这里插入图片描述

RDD

  • RDD是Spark提供的核心抽象,全称为Resillient Distributed Dataset,弹性分布式数据集。
  • RDD在抽象上来说是一种元素集合,包含了数据。他是被分区的,分为多个分区,每个分区分布在集群中的不同节点上,从而让RDD中的数据可以被并行操作。
  • RDD通常通过Hadoop上的文件,即HDFS文件或者hive表,来进行创建;又是也可以通过应用程序中的集合来创建。
  • RDD最重要的特性就是提供了容错性,可以自动从失败节点中恢复过来。如果某个节点上的RDD partition产生节点故障,导致数据丢失,那么RDD会自动通过自己的数据来源重新计算该partition。这一切对使用者透明
  • RDD的数据默认情况下存放在内存中,在内存资源不足时,Spark会自动将RDD数据写入磁盘。
    上图中每一批节点中的每一批数据实际上就是一个RDD,RDD是分布式的,所以数据散落在一批节点上了,每个节点都存储了RDD的部分partition。

Spark的核心编程

  • 定义初始的RDD,也就是定义第一个RDD是从哪里读取数据,hbase、Linux本地文件、程序中的集合
  • 定义对RDD的计算操作,这个在spark里称为算子,map、reduce、flatMap、groupByKey等等
  • 循环往复这个过程,第一个计算完了以后,数据可能就会到了新的一批节点上,也就是变成一个新的RDD,然后再次反复,针对新的RDD定义计算操作
  • 获得最终的数据

用Java编写一个word count

public class WordCountLocal{
	public static void main(String [] args){
		//第一步:创建SparkConf对象,设置Spark应用的配置信息
		//使用setMaster()可以设置Spark应用程序要连接的Spark集群的master节点的url
		//如果设置为local则代表在本地运行
		SparkConf conf = new SparkConf()
				.setAppName("WordCountLocal")
				.setMaster("local");
		//第二部:创建JavaSparkContext对象
		//在Spark中,SparkContext是Spark所有功能的入口,不论是java、scala还是python
		//都必须要有一个SparkContext,它的主要作用,包括初始化Spark应用程序所需的一
		//些核心组件,包括调度器(DAGSchedule、TaskScheduler),还会到Spark Master节点上进行注册
		 //在Spark中,编写不同类型的Spark应用程序,使用的Spark Context是不同的,如果使用Scala,使用
		 //的是原生的SparkContext对象
		 //使用Java,就是JavaSparkContext对象
		 //如果开发Spark SQL程序,那么就是SQLContext、HiveContext等等
		 JavaSparkContext sc  = new JavaSparkContext(conf);
		 //第三步,针对输入源(HDFS、本地文件,等),创建一个初始的RDD
		 //输入源中的数据会打散,分配到RDD的每个partition中,从而形成一个初始的分布式数据集
		 //SparkContext中,用于根据文件类型的输入源创建RDD的方法,叫做textFile()方法
		 //在Java中,创建的普通RDD,都叫做JavaRDD。
		 JavaRDD<String> lines = sc.textFile("E://word.txt")

		//第四步:对初始的RDD进行transformation操作,也就是一些计算操作
		
		JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String,String>(){
				private static final long sericalVersionUID = 1L;
				@Override
				public Iterable<String> call(String line) throws Exception{
					return Arrays.asList(line.split(" "))
				}
		});
		//JavaPairRDD的两个泛型参数,分别代表了tuple元素的第一个值和第二个值类型
		JavaPairRDD<String,Integer> pairs = words.mapToPair(
			new PairFunction<String,String,Integer>(){
				private static final long serialVersionUID=1L;
				@Override
				public Tuple2<String,Integer> call(String word) throws Exception{
					return new Tuple2<String,Integer>(word,1);
				}
			});
			JavaPairRDD<String,Integer> wordCounts = pairs.reduceByKey(
				new Function2<Integer,Integer,Integer>(){
					private static final long serialVersionUID=1L;
					@Override
					public Integer call(Integer v1,Integet v2) throws Exception{
						return v1+v2;
					}
				});
			wordCounts.foreach(new VoidFunction<Tuple2<String,Integer>>(){
				@Override
				public void call(Tuple2<String,Integer> wordcount) throws Exception{
					Syste.out.println(wordCount._1+"-----"+wordCount._2);
				}
			});
			sc.close();
		 }
}

Spark架构

名词解释

Application:用户自定义的Spark程序,用户提交后,Spark为App分配资源将程序转换并执行。
DriverProgram:运行Application的main()函数并且创建SparkContext。
RDD DAG:当RDD遇到Action算子,将之前的所有算子形成一个有向无环图(DAG),再在Spark中转化为Job,提交到集群上进行执行。一个App中可以包含多个job。
Job:一个RDD Graph触发的作业,往往有Spark Action算子触发,在Spark Context中通过runJob方法向Spark提交Job。
Stage:每个Job会根据RDD的宽依赖关系被切分成很多Stage,每个Stage中包含一组相同的Task,这组Task也叫TaskSet。
Task:一个分区对应一个Task,Task执行RDD中对应Stage中包含的算子。Task被封装后放入Executor的线程池中执行。Executor会在线程池中取得一个线程,分配给一个任务,之后任务执行完成,线程池回收线程。
在这里插入图片描述

Yarn的两种提交模式原理

在这里插入图片描述

使用经验:
  • yarn-client用于测试,因为,driver运行在本地客户端,负责调度application,参与集群产生超大量的网络通信,从而导致网卡流量激增,可能会被公司的SA(运维)给警告,好处在于,本地可以看到所有的log,方便调试
  • yarn-cluster,用于生产环境,因为driver运行在nodemanager,没有网卡流量激增的问题,缺点在于,调试不方便,本地用spark-submit提交后,只能通过yarn application-logs application_id这种命令来查看,很麻烦。

Spark程序与作用概念映射

val rawFile = sc.textFile(“README.md”) //Application:1-6行

(将输入的文本文件转化为RDD)

val words = rawFile.flatMap(line=>line.split(" ")) //Job:1-5行

(将文本文件映射为word单词,将文本文件进行分词,转换为一个单词的RDD)

val wordNumber = words.map(w=>(w,1)) //Stage:1-3或4-5行

(将RDD中的每个单词映射为,单词名称为key,value为1的kye-value对)

val wordCounts = wordNumber.reduceByKey(+) //Tasks:1-3或4-5行

(通过reduceBykey操作,将同一个单词的数据进行聚集,进而统计好每一个单词的个数)

wordCounts.foreach(println)

(foreach输出每一个单词的计数)

wordCounts.saveAsTextFile

(saveAsTextFile将结果保存到磁盘)

6行代码对应为一个Application,这个应用程序中有两个Job,1-5行是一个Job,1-4 + 6行是一个Job。

在1-5行这个Job中:
1-3行是一个stage,4-5行是一个stage,因为map和reduceByKey之间要进行shuffle操作。

在这里插入图片描述

TaskScheduler初始化机制

在这里插入图片描述

Master的主备切换机制

Master实际上可以配置两个,Spark原生的standalone模式四支持Master主备切换的。也就是说,当ActiveMaster节点挂掉的时候,可以将StandMaster切换为Active Master
SparkMaster主备切换可以基于两种机制,一种时基于文件系统的,一种时基于zookeeper的。基于文件系统的主备切换机制,需要在ActiveMaster挂掉后,手动切换到Standby Master上;而基于Zookeeper的主备切换机制,可以实现自动切换Master。
这里的Master主备切换机制,指的是在Active Master挂掉后,切换到Standby Master时,Master会做哪些操作。

在这里插入图片描述

Shuffle操作原理(普通)

如下图:
假设有一个节点,上面运行4个ShuffleMapTask,节点上只有2个cpu core
假设有另外一个节点,上面也运行了4个resultTask,现在等着去获取ShuffleMapTask的输出数据,来完成如reduceByKey等操作
每个ShuffleMapTask,都会为每个ResultTask创建一份bucket缓存,以及对应的ShuffleBlockFile磁盘文件

在这里插入图片描述
sparkShuffle操作的两个特点

  • 第一个特点:在spark早期版本,bucket缓存时非常重要的,因为需要将一个ShuffleMapTask所有的数据都写入内粗缓存之后,才会刷新到磁盘。但这就产生一个问题,如果map side数据过多,那么很容易造成内存溢出。所以在spark新版本中进行了优化,默认那个缓存是100kb,然后写入数据达到阈值之后,就将数据刷新到磁盘。
    这种操作的优点是不容易发生内存溢出,缺点是如果内存缓存过小,可能发生过多的磁盘写io操作。所以要根据实际业务情况调整内存缓存大小。
  • 第二个特点:于Mapreduce完全不同的是,mapreduce必须将所有数据有写入磁盘文件之后,才能启动reduce操作来拉取数据。因为mapreduce要实现默认的根据key的排序;但Spark不需要,spark在默认情况下是不会对数据进行排序的。因此ShuffleMapTask每写入一点数据,resultTask就可以拉取一点数据,然后在本地执行聚合函数和算子,进行计算。
    spark的这种好处在于,速度比mapreduce快得多。但也有一个问题,mapreduce提供的reduce是可以处理每个key对应的value的,很方便。但在Spark中,由于这种实时拉取机制,因此提供不了直接处理key对应的value的算子,只能通过groupByKey,先shuffle,有一个MapPartitionRDD,然后用map算子,来处理每个key对应的values。

优化后的shuffle操作原理

在spark新版本中,引入了consolidation机制,提出了shuffleGroup的概念。一个ShuffleMapTask将数据写入ResultTask数量的本地文件不会变,但是当下一个shuffleMapTask运行的时候,可以直接将数据写入之前的shuffleMapTask的本地文件。相当于是对多个ShuffleMapTask进行了合并,从而大大减小了本地磁盘文件数量。
在这里插入图片描述
开启了consolidation机制后的shuffle writer操作,每个节点上的磁盘文件数量,就变成了cpu core的数量ResultTask的数量,比如每个节点有2个cpu,有100个resultTask,那么每个节点上才200个磁盘文件。
但是按照普通的shuffle操作,每个节点上面如果有2个cup core,100个ShuffleMapTask,那么此时就会产生100
100个磁盘文件。
优化之后的shuffle操作,主要通过在sparkConf中设置一个参数。在spark核心编程优化中会有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值