0. spark的基本概念
RDD:是弹性分布式数据集(Resilient Distributed Dataset)的简称,是分布式内存的一个抽象概念,提供了一种高度受限的共享内存模型。
DAG:是Directed Acyclic Graph(有向无环图)的简称,反映RDD之间的依赖关系。
Driver Program:控制程序,负责为Application构建DAG图。
Cluster Manager:集群资源管理中心,负责分配计算资源。
Worker Node:工作节点,负责完成具体计算。
Executor:是运行在工作节点(Worker Node)上的一个进程,负责运行Task,并为应用程序存储数据。
Application:用户编写的Spark应用程序,一个Application包含多个Job。
Job:作业,一个Job包含多个RDD及作用于相应RDD上的各种操作。
Stage:阶段,是作业的基本调度单位,一个作业会分为多组任务,每组任务被称为“阶段”。
Task:任务,运行在Executor上的工作单元,是Executor中的一个线程。
总结:Application由多个Job组成,Job由多个Stage组成,Stage由多个Task组成。Stage是作业调度的基本单位。
1、Spark中groupByKey和reduceByKey的区别
相同点:
1,都作用于 RDD[K,V]
2,都是根据key来分组聚合
3, 默认,分区的数量都是不变的,但是都可以通过参数来指定分区数量
不同点:
1, groupByKey默认没有聚合函数,得到的返回值类型是RDD[ k,Iterable[V]]
2, reduceByKey 必须传聚合函数 得到的返回值类型 RDD[(K,聚合后的V)]
3, groupByKey().map() = reduceByKey
最重要的区别:
reduceByKey 会进行分区内聚合,然后再进行网络传输
groupByKey 不会进行局部聚合
结论:
如果这两个算子,都可以使用, 优先使用reduceByKey
reduceByKey与groupByKey的区别,哪种更具优势?
reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回的结果是RDD[k,v]
groupByKey:按照key进行分组,直接进行shuffle
在实际开发过程中,reduceByKey比groupByKey更建议使用。但需要注意是否会影响业务逻辑。
2、spark RDD中的foreach和collect的区别
collect 在驱动程序中,以数组的形式返回数据集的所有元素;
Spark内有collect方法,是Action操作里边的一个算子,这个方法可以将RDD类型的数据转化为数组, 同时会从远程集群是拉取数据到driver端。
- 遍历RDD中元素,可以使用foreach语句;
- 打印RDD中元素,可用take语句,返回数据集前n个元素,data.take(999).foreach(println);
- 查看其中内容,可用saveAsTextFile方法
foreach操作是直接调迭代rdd中每一条数据
什么是RDD,什么是宽窄依赖
RDD(Resilient Distributed Datasets) ,弹性分布式数据集, 是分布式内存的一个抽象概念,RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,只能通过在其他RDD执行确定的转换操作(如map、join和group by)而创建,然而这些限制使得实现容错的开销很低。对开发者而言,RDD可以看作是Spark的一个对象,它本身运行于内存中,如读文件是一个RDD,对文件计算是一个RDD,结果集也是一个RDD ,不同的分片、 数据之间的依赖 、key-value类型的map数据都可以看做RDD。
窄依赖(Narrow Dependency):指父RDD的每个分区只被子RDD的一个分区所使用,例如map、filter等这些算子 一个RDD,对它的父RDD只有简单的一对一的关系,RDD的每个partition仅仅依赖于父RDD 中的一个partition,父RDD和子RDD的partition之间的对应关系,是一对一的。
宽依赖(Shuffle Dependency):父RDD的每个分区都可能被子RDD的多个分区使用,例如groupByKey、 reduceByKey,sortBykey等算子,这些算子其实都会产生shuffle操作,每一个父RDD的partition中的数据都可能会传输一部分到下一个RDD的每个partition中。此时就会出现,父RDD和子RDD的partition之间,具有错综复杂的关系,这种情况就叫做两个RDD 之间是宽依赖,同时,他们之间会发生shuffle操作。
对spark中的application,job,task,state的认识
RDD任务切分中间分为:Application、Job、Stage和Task
(1)Application:初始化一个SparkContext即生成一个Application;
(2)Job:一个Action算子就会生成一个Job;
(3)Stage:Stage等于宽依赖的个数加1;
(4)Task:一个Stage阶段中,最后一个RDD的分区个数就是Task的个数。
1,Application
- application(应用)其实就是用spark-submit提交的程序。比方说spark examples中的计算pi的SparkPi。一个application通常包含三部分:从数据源(比方说HDFS)取数据形成RDD,通过RDD的transformation和action进行计算,将结果输出到console或者外部存储(比方说collect收集输出到console)。
2,Driver
- Spark中的driver感觉其实和yarn中Application Master的功能相类似。主要完成任务的调度以及和executor和cluster manager进行协调。有client和cluster联众模式。client模式driver在任务提交的机器上运行,而cluster模式会随机选择机器中的一台机器启动driver。
- 一个Spark作业运行时包括一个Driver进程,也是作业的主进程,具有main函数,并且有SparkContext的实例,是程序的人口点。
- 功能:负责向集群申请资源,向master注册信息,负责了作业的调度,负责作业的解析、生成Stage并调度Task到Executor上。包括DAGScheduler,TaskScheduler。
3,Job
- Spark中的Job和MR中Job不一样。MR中Job主要是Map或者Reduce Job。而Spark的Job其实很好区别,一个action算子就算一个Job,比方说count,first等。
4, Task
- Task是Spark中最小的执行单元。RDD一般是带有partitions的,每个partition的在一个executor上的执行任务可以是一个Task。
5, Stage
- Stage概念是spark中独有的。一般而言一个Job会切换成一定数量的stage。各个stage之间按照顺序执行,宽依赖和窄依赖的边界就是stage的划分点。
Repartition和Coalesce关系与区别?
1)关系:
两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:coalesce(numPartitions, shuffle = true)
2)区别:
repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生shuffle
一般情况下增大rdd的partition数量使用repartition,减少partition数量时使用coalesce
spark的部署方式
(1)Local
- 本地模式,运行在一台机器上,通常是练手或者测试环境。
(2)Standalone
- 独立集群模式,构建一个基于Master+Slaves的资源调度集群,Spark任务提交给Master运行。是Spark自身的一个调度系统。
(3)Yarn
- Spark客户端直接连接Yarn,不需要额外构建Spark集群。有yarn-client和yarn-cluster两种模式,主要区别在于:Driver程序的运行节点。(yarn-client的Driver程序运行在客户端,适用于交互、调试,而yarn-cluster的Driver程序运行在由ResourceManager启动的ApplicationMaster中,适用于生产环境)
(4)Mesos
- Spark客户端直接连接Mesos,不需要额外构建Spark集群,用的比较少。
spark提交作业的参数
在提交任务时的几个重要参数
master —— spark的运行环境
executor-cores —— 每个executor使用的内核数,默认为1,官方建议2-5个
num-executors —— 启动executors的数量,默认为2
executor-memory —— executor内存大小,默认1G
driver-cores —— driver使用内核数,默认为1
driver-memory —— driver内存大小,默认512M
spark的算子及作用
spark 算子分为两类:transform与action两类,其中transform类算子只是定义一系列处理逻辑,它并不会触发计算而action 算子会触发整个计算逻辑。
map算子是每次传入一个元素到我们定义的函数中而mapPartitions算子是将整个分区传入我们定义的函数中。
mapPartitions算子定义 即将整个分区传入到我们定义的函数中
flatMap 算子即对传入的每个集合中的每个元素做统一的一个转换操作然后在将所有集合的结果放入统一的一个集合中
filter 是过滤操作,将rdd中不满足条件的数据过滤掉并返回新的Rdd
distinct算子对Rdd中的数据进行去重操作
groupByKey对相同key 的数据进行分组
Action类算子:
1)reduce
对数据进行两两规约操作
2)aggregate
3)collect
4)first
5)take
6)foreach
7) foreachPartitions
8) saveAsTextFile
9) countByKey
10) collectAsMap
11) lookup
12) top
spark的checkpoint机制
Spark中Checkpoint是什么
假设一个应用程序特别复杂场景,从初始RDD开始到最后整个应用程序完成,有非常多的步骤,比如超过20个transformation操作,而且整个运行时间也比较长,比如1-5个小时。此时某一个步骤数据丢失了,尽管之前在之前可能已经持久化到了内存或者磁盘,但是依然丢失了,这是很有可能的。也就是说没有容错机制,那么有可能需要重新计算一次。而如果这个步骤很耗时和资源,那么有点悲剧。
对于一个复杂的RDD,我们如果担心某些关键的,会在后面反复使用的RDD,可能会因为节点的故障,导致持久化数据的丢失,就可以针对该RDD启动checkpoint机制,实现容错和高可用。
在SparkContext中需要调用setCheckpointDir方法,设置一个容错的文件系统的目录,比如HDFS。然后对RDD调用checkpoint方法,之后在RDD所处的job运行结束之后,会启动一个单独的job来将checkpoint过的RDD的数据写入之前设置的文件系统中。进行持久化操作。那么此时,即使在后面使用RDD的时候,他的持久化数据不小心丢失了,但是还是可以从它的checkpoint文件中读取出该数据,而无需重新计算。
Checkpoint与持久化的区别
持久化只是将数据保存在BlockManager中;但是RDD的lineage(血缘关系)是不会变化的
Checkpoint完毕之后,RDD已经没有之前的lineage(血缘关系),而只有一个强行为其设置的CheckpointRDD, 也就是说checkpoint之后,lineage发生了改变
持久化的数据丢失的可能性更大
Checkpoint的数据通常是保存在容错高可用的文件系统中,比如HDFS,所以checkpoint丢失数据的更能性更小
spark的cache和persist的作用
都是做RDD持久化的,cache()和persist()是将数据默认缓存在内存中 ,当然rdd.persist(StorageLevel.DISK_ONLY)也可以存储在磁盘
缓存机制里的cache和persist都是用于将一个RDD进行缓存,区别就是:cache()是persisit()的一种简化方式,cache()的底层就是调用的persist()的无参版本,同时就是调用persist(MEMORY_ONLY)将数据持久化到内存中。如果需要从内存中清除缓存,那么可以使用unpersist()方法。cache () = persist()=persist(StorageLevel.Memory_Only)
另外,cache 跟 persist不会截断血缘关系,checkPoint会截断血缘关系。
cache和persist是lazy的,当第一次遇到Action算子的时侯才会进行缓存或持久化,以后再触发Action会读取、复用缓存的RDD的数据再进行操作。
spark的 几种持久化级别
1.MEMORY_ONLYsvg
使用未序列化的Java对象格式,将数据保存在内存中。若是内存不够存放全部的数据,则数据可能就不会进行持久化。那么下次对这个RDD执行算子操做时,那些没有被持久化的数据,须要从源头处从新计算一遍。这是默认的持久化策略,使用cache()方法时,实际就是使用的这种持久化策略。性能
2.MEMORY_AND_DISK
使用未序列化的Java对象格式,优先尝试将数据保存在内存中。若是内存不够存放全部的数据,会将数据写入磁盘文件中,下次对这个RDD执行算子时,持久化在磁盘文件中的数据会被读取出来使用。
3.MEMORY_ONLY_SER
基本含义同MEMORY_ONLY。惟一的区别是,会将RDD中的数据进行序列化,RDD的每一个partition会被序列化成一个字节数组。这种方式更加节省内存,从而能够避免持久化的数据占用过多内存致使频繁GC。
4.MEMORY_AND_DISK_SER
基本含义同MEMORY_AND_DISK。惟一的区别是,会将RDD中的数据进行序列化,RDD的每一个partition会被序列化成一个字节数组。这种方式更加节省内存,从而能够避免持久化的数据占用过多内存致使频繁GC。
5.DISK_ONLY
使用未序列化的Java对象格式,将数据所有写入磁盘文件中。
6.MEMORY_ONLY_2, MEMORY_AND_DISK_2, 等等
对于上述任意一种持久化策略,若是加上后缀_2,表明的是将每一个持久化的数据,都复制一份副本,并将副本保存到其余节点上。这种基于副本的持久化机制主要用于进行容错。假如某个节点挂掉,节点的内存或磁盘中的持久化数据丢失了,那么后续对RDD计算时还可使用该数据在其余节点上的副本。若是没有副本的话,就只能将这些数据从源头处从新计算一遍了。
谈谈spark的广播变量
广播变量的原理
广播变量,初始的时候,就在Drvier上有一份副本。task在运行的时候,想要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中,尝试获取变量副本;如果本地没有,那么就从Driver远程拉取变量副本,并保存在本地的BlockManager中;此后这个executor上的task,都会直接使用本地的BlockManager中的副本。executor的BlockManager除了从driver上拉取,也可能从其他节点的BlockManager上拉取变量副本。
使用广播变量的好处
1、Driver每次分发任务的时候会把task和计算逻辑的变量发送给Executor。不使用广播变量,在每个Executor中有多少个task就有多少个Driver端变量副本。这样会导致消耗大量的内存导致严重的后果。
2、使用广播变量的好处,不需要每个task带上一份变量副本,而是变成每个节点的executor才一份副本。这样的话, 就可以让变量产生的副本大大减少;
spark的foreach和foreachPartition的区别 (map和mapPartition)
foreach是针对于RDD的每个元素来操作的,foreachPartition是针对于RDD的每个分区进行操作的
从优化层面讲:foreachPartition用于存储大量结果数据的场景,可以一个分区对应一个数据库的连接,这样就可以减少很多数据库的连接
- map():每次处理一条数据。
- mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM。
- 开发指导:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。
sparkSQL如何整合hive
Spark SQL整合hive就是获取hive表中的元数据信息(在mysql中),然后通过Spark SQL来操作数据。
2. SparkSQL与Hive共用元数据
2.1 开启Hive元数据服务
2.1.1 修改hive配置
在hive的 hive-site.xml 修改一行配置,增加了这一行配置之后,以后在使用hive之前都 需要先启动元数据服务
开启hive元数据服务的目的: 是让spark sql 可以获取到hive的元数据
2.1.2 启动hive元数据服务
启动Hive元数据服务,并验证Hive是否可用
启动命令:
hive --service metastore
拷贝hive-site.xml和mysql驱动
将hive-site.xml 复制到spark conf目录下
将 mysql 驱动包复制到saprk jars目录下
使用yarn-client模式启动
spark中RDD、DataFrame、DataSet的区别
共同点:
1.RDD、 DataFrame、DataSet都是spark平台下的分布式数据集,为处理超大型数据提供便利;
2.三者都有惰性机制,在进行创建、转换时,不会立即执行,只有在遇到行动算子的时候才会开始计算;
3.在对DataFrame和DataSet进行操作时,许多操作都需要导入:import spark.implicits._ 包;
4.三者都会根据Spark的内存情况进行自动缓存计算,这样即使数据量很大,也不会担心内存溢出;
RDD
1.RDD 一般和spark mllib(后面解释)同时使用
2.RDD不支持sparksql操作
DataFrame
1.DataFrame每一行的类型固定为Row,每一列的值无法直接访问,只有通过解析才能获取各个字段的值;
2.DataFrame和DataSet一般不与spark mllib同时使用
3.DataFrame和DataSet均支持sparksql操作
DataSet
1.DataFrame和DataSet拥有完全相同的成员函数,区别只是每一行的数据类型不同,DataFrame其实就是DataSet的一个特例,type DataFrame = DataSet[Row]
2.DataFrame也可以叫DataSet[Row],每一行的类型为Row,每一行究竟有哪些字段,各个字段的类型是什么无从得知;而DataSet每一行是什么类型是不一定的,自定义case class之后可以很自由的获得每一行中的信息。
spark与MapReduce的Shuffle的区别
MapTask和ReduceTask
shuffle过程
Map阶段shuffle:分区->排序->合并
①数据从环形缓冲区溢写到磁盘前,需要先进行分区,然后区内排序
②环形缓存区数据到达阈值(80%),会以小文件的形式溢写到磁盘,此过程可以开启combiner
③将溢写的小文件按照相同分区进行merge
Reduce阶段shuffle:拷贝数据->排序->合并
①一个ReduceTask负责一个分区数据,需从多个MapTask的同一个分区拷贝数据到机器。
②将拷贝过来的数据优先存储在内存,次之磁盘,然后排序,合并做到数据分区内有序
shffle的意义:只有存在reduce才有shuffle,shuffle的意义就是给reduce提供服务。
spark的shuffle
①spark的某些算子会触发shuffle,出现shuffle的目的是在不同分区间重新分配数据。
②shuffle过程数据是跨机器传输的,消耗大量的网络io和序列化,消耗性能。
③shuffle后不能保证新的分区的数据是有序的。区别于MR ( MR的shuffle后区内的数据是有序的 )
但是可以调用排序的算子,使得数据区内有序。
④产生shuffle的算子都是分两步执行,mapTask组织数据(shuffle write), reduceTask(shuffle read)
⑤spark的mapTask优先将数据写入内存,内存不足,将数据区内有序,溢写到磁盘
会产生shuffle的算子
①repartition 和 coalesce 重新计算分区的算子。
②??ByKey:除了countByKey,都会产线shuffle
③cogroup 和 join
性能的影响
shuffle就是将数据在不同分区间进行聚合分配,集群的多节点的数据交换,会涉及到磁盘I/O,序列化,网络I/O,很消耗性能。
spark中的shuffle耗时,消耗性能,应该尽量避免!
spark中的shuffle和MapReduce的shuffle的功能一致,跨机器传输数据,细节略有不同。
Spark的Shuffle
Spark的Shuffle是在MapReduce Shuffle基础上进行的调优。其实就是对排序、合并逻辑做了一些优化。在Spark中Shuffle write相当于MapReduce 的map,Shuffle read相当于MapReduce 的reduce。
Spark丰富了任务类型,有些任务之间数据流转不需要通过Shuffle,但是有些任务之间还是需要通过Shuffle来传递数据,比如宽依赖的group by key以及各种by key算子。宽依赖之间会划分stage,而Stage之间就是Shuffle
spark的提交作业的流程
1、在 YARN Clinet 模式下,Driver 在任务提交的本地机器上运行;
2、Driver 启动后会和 ResourceManager 通讯申请启动 ApplicationMaster;
3、随后 ResourceManager 分配 container,在合适的 NodeManager 上 启动 ApplicationMaster,此时的 ApplicationMaster 的功能相当于一个 ExecutorLauncher,只负责向 ResourceManager 申请 Executor 内存;
4、ResourceManager 接到 ApplicationMaster 的资源申请后会分配 container,然后 ApplicationMaster 在资源分配指定的 NodeManager 上 启动 Executor 进程;
5、Executor 进程启动后会向 Driver 反向注册,Executor 全部注册完成后 Driver 开始执行 main函数;
6、之后执行到 Action 算子时,触发一个 job ,并根据 宽依赖 开始划分 stage,每个stage生成对应的 taskset,之后将 task 分发到各个 Executor 上执行。
spark的RDD分区数是由什么决定的
1、参数配置(并行度)
分区的默认个数等于对spark.default.parallelism的指定值
2、根据父rdd的reduceTask数量
3、读取hdfs的文件生成的rdd
rdd分区的数量等于hdfs的文件的block
4、sparkStreaming生成的rdd
根据block interval,batch interval的时间决定
default.block.interval=200ms
批次时间在sparkContext的参数中设定
partitions = batch interval / block interval
5、如果是将Driver端的Scala集合并行化创建RDD,并且没有指定RDD的分区,RDD的分区就是为该app分配的中的核数cores(–total-executor-cores),可以充分利用计算资源,提高并行度
spark为什么比MR快
1、基于内存,减少大量的磁盘io操作。
mapreduce任务每次都会把结果数据落地到磁盘,
后续有其他的job需要依赖于前面job的输出结果,
这里就需要进行大量的磁盘io操作,获取前面job的输出结果。性能非常低
spark任务的输出结果可以保存在内存中,
后续有其他的job需要依赖于前面job的输出结果,
这里就只需要直接从内存中获取得到,大大减少磁盘io操作。
2、mapreduce任务启动进程,spark任务启动线程。线程可以重复使用同一个进程,大大减少资源浪费。
mapreduce任务它是以进程的方式运行在yarn集群中,
比如说一个mapreduce任务有100个MapTask,
后期需要运行这100个task,就需要启动100个进程。
spark任务它是以线程的方式运行在worker节点的executor进程中,
比如说一个spark任务有100个MapTask,这里后期需要运行100个线程就可以了。
可以这样极端一点:只需要启动一个进程,在一个进程中运行100个线程就可以了.
开启一个进程比开启一个线程需要的时间和资源调度肯定是不一样,开启一个进程需要的时间远远大于线程..