Spark算子

目录

什么是算子

1.Transformation 转换算子

1.1 转换算子

1.2 转换算子是干什么的

1.3 转换算子分类

1.4 转换算子详解

1.4.1 map

1.4.2 flatMap

1.4.3 distinct

1.4.5 glom

1.4.6 union

1.4.7 cartesian

1.4.8 groupBy

1.4.9 subtract

1.4.10 sample

1.4.11 mapValues

1.4.12 reduceByKey

1.4.13 partitionBy

1.4.14 join

1.4.15 sortyByKey(sortBy)

2.Action

2.1 foreach

2.2 saveAsTextFile

2.3 saveAsObjectFile

2.4 colloect

2.5 collectAsMap

2.6 lookup

2.7 count

2.8 top

2.9 reduce

2.10 fold

3.对应分区

1.转换算子

1.1 value 类型

1.2 key-value类型

2.行动算子

4.Spark流程

1.创建SparkConf对象

2.创建SparkContext对象;

3.创RDD

4.触发执行

5.关闭程序


Spark是专为大规模数据处理而设计的快速通用的计算引擎;拥有Hadoop MapReduce所具有的优点,但是运行速度却比MapReduce有很大的提升。

Spark是基于内存进行数据处理的,MapReduce是基于磁盘进行数据处理的。

什么是算子

RDD的方法和Scala集合对象的方法不一样,集合对象的方法都是在同一个节点的内存中完成的。RDD的方法可以将计算逻辑发送到Executor端(分布式节点)执行。

为了区分不同的处理效果,所以将RDD的方法称为算子,RDD的方法外部的操作都是在Driver端执行的,而方法内部的逻辑代码是在Executor端执行。

Spark算子大致可以分为4类:创建算子、转换算子、缓存算子、行动算子

1.Transformation 转换算子

1.1 转换算子

不触发提交作业,只是完成作业中间过程处理;Transformation 操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运算。Transformation参数类型为value或者key-value的形式;

转换算子是延迟执行的,也叫懒加载执行

1.2 转换算子是干什么的

就是对RDD进行操作的接口函数,其作用是将一个或多个RDD变换成新的RDD。

Spark中,在利用创建算子生成RDD后,数据处理的算法设计和程序编写的最关键部分,就是利用变换算子对原始数据产生的RDD进行一步一步的变换,最终得到期望的计算结果。

1.3 转换算子分类

可分为两类:

  • 对Value型RDD进行变换的算子;

  • 对Key/Value型RDD进行变换算子。在每个变换中有仅对一个RDD进行变换的,也有是对两个RDD进行变换的。

1.4 转换算子详解

1.4.1 map

将一个RDD中的每个数据项,通过map中的函数映射变为一个新的元素。 输入分区与输出分区一对一,即:有多少个输入分区,就有多少个输出分区,分区数不会改变。

scala> val a = sc.parallelize(List("dog","salmon","salmon","rat","elephant"),3)
a: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at :24
scala> a.partitions.size
res0: Int = 3
// glom的作用是将同一个分区里的元素合并到一个array里
// glom属于Transformation算子:这种变换并不触发提交作业,完成作业中间过程处理。
scala> a.glom.collect
res2: Array[Array[String]] = Array(Array(dog), Array(salmon, salmon), Array(rat, elephant))
scala> a.map(_.length).glom.collect
res4: Array[Array[Int]] = Array(Array(3), Array(6, 6), Array(3, 8))

1.4.2 flatMap

同Map算子一样,最后将所有元素放到同一集合中,分区数不会改变。注意:针对Array[String]类型,将String对象视为字符数组。将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的 RDD 的每个集合中的元素合并为一个集合。

lines.flatMap{lines => {
      lines.split(" ")

Spark算子_第1张图片Spark算子_第2张图片

1.4.3 distinct

将RDD中重复元素做去重处理。

scala> a.distinct.collect
res22: Array[String] = Array(rat, salmon, elephant, dog)

1.4.4 mapPartitions

mapPartitions 函 数 获 取 到 每 个 分 区 的 迭 代器,在 函 数 中 通 过 这 个 分 区 整 体 的 迭 代 器 对整 个 分 区 的 元 素 进 行 操 作。 内 部 实 现 是 生 成 -------f (iter)=>iter.f ilter(_>=3)。

Spark算子_第3张图片Spark算子_第4张图片

1.4.5 glom

glom函数将每个分区形成一个数组,内部实现是返回的GlommedRDD。 下图中的每个方框代表一个RDD分区。 该图表示含有V1、 V2、 V3的分区通过函数glom形成一数组Array[(V1),(V2),(V3)]。

Spark算子_第5张图片Spark算子_第6张图片

1.4.6 union

使用 union 函数时需要保证两个 RDD 元素的数据类型相同,返回的 RDD 数据类型和被合并的 RDD 元素数据类型相同,并不进行去重操作,保存所有元素。如果想去重可以使用 distinct()(并集)。

Spark算子_第7张图片Spark算子_第8张图片

1.4.7 cartesian

对 两 个 RDD 内 的 所 有 元 素 进 行 笛 卡 尔 积 操 作。 操 作 后, 内 部 实 现 返 回CartesianRDD。

1.4.8 groupBy

将元素通过函数生成相应的 Key,数据就转化为 Key-Value 格式,之后将 Key 相同的元素分为一组。

1.4.9 subtract

subtract相当于进行集合的差操作,RDD 1去除RDD 1和RDD 2交集中的所有元素。

1.4.10 sample

sample 将 RDD 这个集合内的元素进行采样,获取所有元素的子集。用户可以设定是否有放回的抽样、百分比、随机种子,进而决定采样方式。内部实现是生成 SampledRDD(withReplacement, fraction, seed)。

1.4.11 mapValues

针对(key, Value)型数据中的value进行map操作,而不对key进行处理。

Spark算子_第9张图片Spark算子_第10张图片

1.4.12 reduceByKey

reduceByKey的作用对像是 (key, value)形式的rdd,而reduce有减少、压缩之意,reduceByKey的作用就是对相同key的数据进行处理,最终每个key只保留一条记录。. 保留一条记录通常有两种结果。

Spark算子_第11张图片Spark算子_第12张图片

1.4.13 partitionBy

partitionBy函数对RDD进行分区操作;

partitionBy(partitioner:Partitioner)。

Spark算子_第13张图片Spark算子_第14张图片

1.4.14 join

join 对两个需要连接的 RDD 进行 cogroup函数操作,将相同 key 的数据能够放到一个分区,在 cogroup 操作之后形成的新 RDD 对每个key 下的元素进行笛卡尔积的操作,返回的结果再展平,对应 key 下的所有元组形成一个集合。最后返回 RDD[(K, (V, W))]。

1.4.15 sortyByKey(sortBy)

作用再(Key, Value)格式的数据上,根据Key进行升序或降序排序。

2.Action

Action行动算子。

本质上在Action算子中通过SparkContext触发SparkContext提交job作业。Action 算子会触发 Spark 提交作业(Job),并将数据输出 Spark系统。

2.1 foreach

foreach对RDD中的每个元素都应用f函数操作,不返回RDD和Array,而返回Uint.也就是遍历。

2.2 saveAsTextFile

函数将数据输出,存储到HDFS的制定目录下。

2.3 saveAsObjectFile

将分区中的每10个元素组成一个Array,然后将这个Array序列化,映射为Null,BytesWritable(Y)的元素,写入HDFS为SequenceFile的格式;

2.4 colloect

相当于toArray,collect将分布式的RDD返回为一个单机的scala Array数组,在这个数组上运用scala的函数式操作;

Spark算子_第15张图片

 左侧方框代表 RDD 分区,右侧方框代表单机内存中的数组。通过函数操作,将结果返回到 Driver 程序所在的节点,以数组形式存储。

2.5 collectAsMap

collectAsMap对(K,V)型的RDD数据返回一个单机HashMap; 对于重复K的RDD元素,后面的元素覆盖前面的元素

Spark算子_第16张图片

2.6 lookup

Lookup函数对(Key,Value)型的RDD操作,返回指定Key对应的元素形成的Seq。

这个函数处理优化的部分在于,如果这个RDD包含分区器,则只会对应处理K所在的分区,然后返回由(K,V)形成的Seq。 如果RDD不包含分区器,则需要对全RDD元素进行暴力扫描处理,搜索指定K对应的元素。

lookup(key:K):Seq[V]

Spark算子_第17张图片

2.7 count

count(计数器)返回整个RDD的元素个数。

Spark算子_第18张图片

2.8 top

top可返回最大的k个元素

  • top返回最大的k个元素。

  • take返回最小的k个元素。

  • takeOrdered返回最小的k个元素,并且在返回的数组中保持元素的顺序。

  • first相当于top(1)返回整个RDD中的前k个元素,可以定义排序的方式Ordering[T],返回的是一个含前k个元素的数组.

2.9 reduce

reduce函数相当于对RDD中的元素进行reduceLeft函数的操作;

reduceLeft先对两个元素进行reduce函数操作,然后将结果和迭代器取出的下一个元素进行reduce函数操作,直到迭代器遍历完所有元素,得到最后结果。在RDD中,先对每个分区中的所有元素的集合分别进行reduceLeft。 每个分区形成的结果相当于一个元素,再对这个结果集合进行reduceleft操作;

Spark算子_第19张图片

2.10 fold

fold和reduce的原理相同,但是与reduce不同,相当于每个reduce时,迭代器取的第一个元素是zeroValue。

Spark算子_第20张图片

3.对应分区

1.转换算子

1.1 value 类型

细类型 算子
输入分区与输出分区一对一型 map flatMap mapPartitions glom
输入分区与输出分区多对一型 union cartesain
输入分区与输出分区多对多型 groupBy
输出分区为输入分区子集型 filter distinct substract sample takeSample
Cache型 cache persist

1.2 key-value类型

细类型 算子
输入分区与输出分区一对一 mapValues
对单个RDD或两个RDD聚集 单个RDD聚集: combineByKey reduceByKey partitionBy; 两个RDD聚集: Cogroup
连接 join leftOutJoin和 rightOutJoin


2.行动算子

细类型 算子
无输出 foreach
HDFS saveAsTextFile saveAsObjectFile
Scala集合和数据类型 collect collectAsMap reduceByKeyLocally lookup count top reduce fold aggregate

4.Spark流程

1.创建SparkConf对象

  • 可以设置Application name;
  • 可以设置运行模式及资源需求;

2.创建SparkContext对象;

  • SparkContext向资源管理器申请运行Executor资源,并启动StandaloneExecutorbackend;
  • Executor向SparkContext申请Task;
  • SparkContext将程序分发给Executor;
  • SparkContext构建成DAG图,将DAG图分解成Stage、将Taskset发送给Task Scheduler,最后由Task Scheduler将Task发送给Executor运行;
  • Task在Excutor上运行,运行完释放所有的资源;

3.创RDD

基于Spark的上下文创建一个RDD,对RDD进行处理;


4.触发执行

应用程序中y有Action累算子来触发Transformation类算子执行;

5.关闭程序

关闭Spark上下文对象SparkContext;

5 spark分区

Spark RDD 是一种分布式的数据集,由于数据量很大,因此要它被切分并存储在各个结点的分区当中。从而当我们对RDD进行操作时,实际上是对每个分区中的数据并行操作。Spark算子_第21张图片

每一个过程的任务数,对应一个inputSplit1, Partition输入可能以多个文件的形式存储在HDFS上,,每个File都包含了很多块,(128M切分),称为Block。

当Spark读取这些文件作为输入时,会根据具体数据格式对应的InputFormat进行解析,一般是将若干个Block合并成一个输入分片,称为InputSplit,注意InputSplit不能跨越文件

随后将为这些输入分片生成具体的Task。InputSplit与Task是一一对应的关系。然后这些具体的Task每个都会被分配到集群上的某个节点的某个Executor去执行。

  • 每个节点可以起一个或多个Executor。
  • 每个Executor由若干core组成,每个Executor的每个core一次只能执行一个Task。
  • 每个Task执行的结果就是生成了目标RDD的一个partiton。

1、partition的数目设置:

对于数据读入阶段,例如sc.textFile,输入文件被划分为多少InputSplit就会需要多少初始Task。在Map阶段partition数目保持不变。

在Reduce阶段,RDD的聚合会触发shuffle操作,聚合后的RDD的partition数目跟具体操作有关,例如repartition操作会聚合成指定分区数,还有一些算子是可配置的。

RDD在计算的时候,每个分区都会起一个task,所以rdd的分区数目决定了总的task数目。申请的计算节点(Executor)数目和每个计算节点核数,决定了你同一时刻可以并行执行的task。

比如:RDD有100个分区,那么计算的时候就会生成100个task,设置task间并行的参数是conf spark.sql.shuffle.partitions=100

你的资源配置为10个计算节点,(执行器excutor)  --num-executors 10    默认为2一般设置在50-100之间,每个2个核,executor-cores 2   一般 2~4 为宜。同一时刻可以并行的task数目为20,计算这个RDD就需要5个轮次。Task被执行的并发度 = Executor数目 * 每个Executor核数(=core总个数)

如果计算资源不变,你有101个task的话,就需要6个轮次,在最后一轮中,只有一个task在执行,其余核都在空转。

如果资源不变,你的RDD只有2个分区,那么同一时刻只有2个task运行,其余18个核空转,造成资源浪费。

这就是在spark调优中,增大RDD分区数目,增大任务并行度的原因。

2、spark分区 什么时候增加的,增加有什么用?
接下来的描述,是针对于sparksql(也就是把数据加载成Dataset之后再处理)来说的。

1.增加分区数,可以增加并行度,当spark申请的cpu核心足够的情况下,可以同时跑不同分区的数据(因为一个分区的数据,只能由一个核心来跑,不能多个)

2.手动增加,使用repartition来将所有数据打散

3.自动增加

spark有个参数:spark.sql.shuffle.partitions,默认值为200。也就是说当触发shuffle逻辑的时候,数据会自动分为200个分区运行,但是在数据量大的情况下,每个分区的数据量太大,而且假设spark申请到了300个核心,但是因为分区数只有200,会导致只有200个核心在运行,另外100个核心在空转(虽然占用资源但是却不干活)。所以可以将该参数设置为500甚至更大,来增加分区数和并行度。
 

你可能感兴趣的:(spark,spark,scala,大数据)