运行架构
Driver:是spark的驱动器,执行main方法,负责实际代码的执行工作
master:主要分组资源的调度和分配
worker:由master分配资源对数据进行并行的处理和计算
executor:是整个集群中的专门用来计算的节点
并行度:多个任务同时执行,整个集群并行执行任务的数量称之为并行度
三大数据结构
RDD——弹性分布式数据集
RDD的数据只有在调用collect时候才会被真正执行,封装了计算的逻辑,全都是功能的扩展
RDD是不会保存数据的,IO是可以保存部分数据
RDD封装了计算逻辑,是不可以改变的,想要该表,只能产生新的RDD,在新的RDD里面封装了计算逻辑
优势:
存储的弹性:内存和磁盘的自动切换
容错的弹性:数据丢失可以自动恢复
计算的弹性:计算出错重试机制
分片的弹性:可根据需要重新分片
spark读取文件的方式,底层使用的就是hadoop的读取方式
数据分区的分配
1.数据以行为单位进行读取(采用hadoop的读取方式进行)
2.数据读取时候以偏移量为单位
3.数据分区的偏移量范围的计算
分为转换算子和行动算子
转换算子:功能的补充和封装,产生新的RDD算子
行动算子:是对触发作业的执行和调度
转换算子
Map算子
一个一个处理,有点类似IO流
mapPartitions算子
一次性把一个分区里面的数据拿过来一起做梳理,而不是像map那样一个一个处理,
会将整个分区里面的数据加载到内存中,但是在内存较小,数据量较大的情况下,就容易造成内存溢出
map 和 mapPartitions 的区别?
map算子是分区内一个数据一个数据的执行,类似于串行操作,而mappartition算子是以分区为单位进行的批出操作
map主要目的是将数据源中的数据进行转换和改变,但是不会减少或者增加数据的条目,mapartition算子需要传递一个迭代器,返回一个迭代器。元素的个数可以按照需求而定,可以增加或者减少数据
mapPartitionWithIndex算子
增加了分区号,将待处理的数据以分区的形式进行统一处理,并且在处理的同时可以获取当前分区的索引
flatMap算子
对数据做一个扁平化操作,将数据打散
golm算子
返回通过将每个分区中的所有元素合并到一个数组中而创建的RDD,转换之后分区不变,
groupBy算子
groupBy会将数据打乱打散,重新组合,整个操作称之为shuffle
filter算子
将数据按照特定的规则进行筛选过滤,输出符合规则的
sample算子
从大规模数据中抽取数据
第一个参数:抽取数据后是否将数据放回
第二个参数:数据源中每条数据被抽取的概率
(如果抽取不放回,表示数据源中每条数据被抽取的概率)
(如果抽取放回的场合:表示数据源中每条数据被抽取的可能次数)
第三个参数:表示随机算法的种子
coalesce算子
根据数量缩减分区,用于大数据集过滤后,提高小数据集的执行效率,减小分区的数量
不会打乱数据重组,只会缩减分区
repartition算子
扩大分区,内部还是执行的coalesce算子,只是默认执行shuffle操作,没有shuffle操作的话,就没有意义
sortBy算子
按照一定的规则进行排序
交集并集差集拉链
intersection、union、subtract、zip都是针对两个value类型的
partitionBy
reduceByKey
按照指定的键,对value做聚合
支持分区内预聚合,可以有效减少shuffle时落盘的数据量
分区内和分区间计算规则是相同的
groupByKey
按照指定的键,将value聚合成一个迭代器 (a,(1,2,3))
类比groupBy,(a,((a,1), (a,2), (a,3)))
reduceByKey对比groupByKey
都存在shuffle的操作,但是reduceByKey可以在shuffle前对分区内的数据进行预聚合,这样会减少落盘的数据量。
groupByKey只是进行分组,不存在数据量减少的问题
reduceByKey性能高
reduceByKey包含分组和聚合的功能,GroupByKey只能分组,不能聚合
aggregateByKey
分区内计算和分区间的计算规则可以不同,自己定义
第一个参数:表示计算的初始值
第二个参数列表:
分区内的计算规则
分区间的计算规则
foldByKey
如果分区内和分区间的计算规则相同了,那么就是用foldByKey算子
combinByKey
reduceByKey、foldByKey、aggregateByKey、combineByKey 的区别?
序列化
在scala函数式编程中,算子内经常会用到算子外的数据,这样就形成了闭包的效果,
血缘关系
两个相邻的RDD之间的依赖关系
(宽、窄依赖是根据上下游RDD的分区而言的)
宽依赖:上游一个RDD的partition可以被下游多个RDD的partition依赖
窄依赖:上游一个RDD的partition可以被下游一个RDD的partition依赖
RDD不会保存数据,只会保存血缘关系。提高容错性,将RDD之间的关系恢复重新进行读取
阶段的划分
♥ **阶段:划分stage的依据就是RDD之间的宽窄依赖,遇到宽依赖就划分stage,每个stage包含一个或者多个task任务,stage是由一组并行的task组成;**切割规则:遇到宽依赖就切割stage(遇到一个shuffle就转为一个新的阶段)
阶段的划分等于shuffle依赖的数量+1
spark设计依赖关系的目的?
为了能够spark并行计算,是划分stage的依据
为了构建血缘关系进行RDD的容错,一个分区数据丢失,只需要从父RDD对应的一个分区重新计算即可
任务的划分
每个阶段的最后一个RDD的分区的数量就是该阶段的Task的数量,所以整个过程中Task的总的数量之和,就是每一个阶段的最后一个RDD的分区数之和
任务的调度
移动数据不如移动计算
计算和数据的位置存在不同级别,这个级别称之为本地化级别
进程本地化:数据和计算在同一个进程中
节点本地化:数据和计算在同一个节点中
机架本地化:数据和计算在同一个机架中
Stage、Job、和Task的关系
job是以Action方法为界,遇到一个action方法触发一个job
stage是job的子集,以RDD宽依赖为界,遇到shuffle做一次划分
Task是Stage的子集,以并行度来衡量,分无数是多少,则有多少个task
Application
new一个sparkcontext就会生成一个application
Job
一个行动算子就会产生一个Job
Stage
stage等于宽依赖的个数+1
Task
一个Stage中,最后一个RDD的分区个数就是Task的个数
持久化
RDD中不能存储数据
检查点
将血缘关系写入到磁盘中,增加容错
持久化和检查点的区别?
最主要的区别在于,持久化只是将数据保存在内存或磁盘文件中,RDD 的依赖关系没有改变。而 checkpoint 执行完之后,RDD 的依赖关系已经改变了,没有之前的依赖关系了,只有一个 checkpointRDD,checkpoint 之后 RDD 的依赖关系就变了。
持久化:
cache:将数据临时写入到内存中进行数据重用
persist:将数据临时存入次磁盘中进行数据重用,作业执行完毕,临时保存的数据也会丢失
检查点:
数据长久的存储在磁盘文件中进行数据重用,一般情况下需要和cache联合使用
RDD中不存储数据,如果一个RDD需要重复使用,那么需要从头来执行,持久化就是解决这个问题,而持久化又分为chche和persist来实现,但是chche底层是调用无参的persist实现;这个方法并不是被调用时立即执行缓存,而是触发后面的行动算子时执行;
检查点就是将RDD中间结果写入磁盘中,
累加器——分布式共享只写变量
spark中master、worker、driver、executor的区别?
一个spark集群有master和worker节点,master节点管理worker节点
driver进行就是application的main函数并且构建sparkContext对象。driver会向集群申请executor,每个executor都占用一定数量的cpu和memory
广播变量——分布式共享只读变量
广播变量可以将闭包的数据保存到executor的内存中,不能更改,只能读
Spark—SQL
rdd、dataframe、dataset的区别
rdd只关注数据本身,类似于文本数据
dataframe类似二维表格,有对应的列名
dataset对比dataframe,是强类型的,每一列都有对应的类型
SparkCore内核
阶段的划分
♥ **阶段:划分stage的依据就是RDD之间的宽窄依赖,遇到宽依赖就划分stage,每个stage包含一个或者多个task任务,stage是由一组并行的task组成;**切割规则:遇到宽依赖就切割stage(遇到一个shuffle就转为一个新的阶段)
阶段的划分等于shuffle依赖的数量+1
spark设计依赖关系的目的?
为了能够spark并行计算,是划分stage的依据
为了构建血缘关系进行RDD的容错,一个分区数据丢失,只需要从父RDD对应的一个分区重新计算即可
任务的划分
每个阶段的最后一个RDD的分区的数量就是该阶段的Task的数量,所以整个过程中Task的总的数量之和,就是每一个阶段的最后一个RDD的分区数之和
任务的调度
移动数据不如移动计算
计算和数据的位置存在不同级别,这个级别称之为本地化级别
进程本地化:数据和计算在同一个进程中
节点本地化:数据和计算在同一个节点中
机架本地化:数据和计算在同一个机架中
Job的划分
job是以action算子为界,一个action算子产生一个job
Stage、Job、和Task的关系
job是以Action方法为界,遇到一个action方法触发一个job
stage是job的子集,以RDD宽依赖为界,遇到shuffle做一次划分
Task是Stage的子集,以并行度来衡量,分无数是多少,则有多少个task
Shuffle机制
如果shuffle过程中落盘数量减少,那么可以提高性能
算子如果存在预聚合功能,可以提高性能
小文件过多,会导致性能急剧下降
大数据处理框架Spark
在hadoop的MR中,每个task对应一个进程,每个task以jvm的方式来运行,
在spark中,每个task对应的jvm中的一个线程,而一个jvm可能同时运行了多少个task,因此jvm的内存空间由task共享
task以线程的方式运行在executor进程中,执行具体的计算任务
每个worker进程上存在一个或者多个executorRunner对象,每个executorRunner对象管理一个executor(持有一个线程池,每个线程执行一个task)
每个spark应用启动一个driver和多个executor,每个executor里面运行的task都属于同一个spark应用
讨论:spark为什么以线程的方式运行而不以进程的方式运行?
每个map/reduce以一个java进程方式进行,这样的好处是task之间相互独立,每个task独享进程资源,不会相互干扰,而且监控管理比较方便;坏处是task之间不方便共享数据
执行过程
逻辑处理流程
首先建立DAG行的逻辑处理流程,然后根据逻辑处理流程生成物理执行计划,物理执行计划中包含具体的计算任务,最后spark将task分配到多台机器上执行;逻辑处理流程图只是表示输入输出、中间数据、以及之间的依赖关系,并不涉及具体的计算任务
物理执行计划
该阶段表示具体的执行的处理流程的,也就是如何生成具体的执行任务的
Spark逻辑处理流程
RDD只是一个逻辑概念,在内存中并不会真正地为某个RDD分配存储空间,RDD中的数只会在计算中产生,而且在计算完成后就会消失;
RDD分为行动算子和转换算子,行动算子一般是对数据结果进行后处理,产生输出结果,并且会触发spark提交job真正执行数据处理任务;
为什么会有RDD?
数据的输入输出、中间数据本身具有不同的类型,而且是由不同的计算逻辑得到的,可能具有不同的依赖关系;因此需要多种类型RDD来表示不同的数据类型、不同的计算逻辑,以及不同的数据依赖,RDD应用而生;
如何确定新生成的RDD的分区个数?
在spark中,新生成的RDD的分区个数由用户个夫RDD共同决定,如果指定,则使用人为指定的分区个数,若不指定分区个数,则一般取夫RDD的分区个数个最大值
新生成的RDD与父RDD分区之间是什么关系?
是一个逻辑概念,在内存中并不会真正地为某个RDD分配存储空间,RDD中的数只会在计算中产生,而且在计算完成后就会消失;
RDD分为行动算子和转换算子,行动算子一般是对数据结果进行后处理,产生输出结果,并且会触发spark提交job真正执行数据处理任务;
为什么会有RDD?
数据的输入输出、中间数据本身具有不同的类型,而且是由不同的计算逻辑得到的,可能具有不同的依赖关系;因此需要多种类型RDD来表示不同的数据类型、不同的计算逻辑,以及不同的数据依赖,RDD应用而生;
如何确定新生成的RDD的分区个数?
在spark中,新生成的RDD的分区个数由用户个夫RDD共同决定,如果指定,则使用人为指定的分区个数,若不指定分区个数,则一般取夫RDD的分区个数个最大值
新生成的RDD与父RDD分区之间是什么关系?
分区之间依赖关系既与转换算子有关,也与RDD的分区个数有关;此处引出了宽依赖与窄依赖,窄依赖:如果新生成的子RDD中每个分区都依赖父RDD中的一部分分区,那么这个分区依赖关系被称为窄依赖;宽依赖:新生成的子RDD中的分区依赖父RDD中的每个分区的一部分