spark面试题部分总结

Spark


1.能跟我谈谈Spark吗?

定义

Spark是一种基于内存的快速、通用、可扩展的大数据分析(计算)引擎。

Spark的诞生仅仅是为了替换早期的Hadoop的MapReduce计算引擎。Spark并没有存储解决方案,在Spark的架构中,底层存储方案依然延续Hadooop的HDFS/Hive/HBase.由于Hadoop的MapReduce是大数据时代出现的第一代大数据分析工具,因为出现比较早仅仅为了满足大数据计算的刚性需求(能够做到对大数据的计算,并且可以保证在一个合理的时间范围内)。因此伴随着科技的进步,互联网的快速发展,人们开始对大数据计算提出了更苛刻要求

我个人认为,Spark是目前最流行的大数据处理框架之一,其优秀的性能、易用性和灵活性得到了广泛的认可。以下是我对Spark的一些看法:

  1. 高效性:Spark具有较高的处理速度和较低的延迟,使其在大数据处理、数据挖掘和机器学习等方面取得了广泛的应用,在更短的时间内完成了对数据的处理,大大提高了数据分析的效率和准确性。

  2. 易用性:Spark提供了各种API和语言支持,包括Java、Scala和Python等,这使得数据分析人员能够方便地进行大数据处理。此外,Spark的交互式Shell也使其易于使用和调试。

  3. 灵活性:Spark的灵活性非常高,可以处理包括批处理和流式处理在内的各种类型的数据。Spark还提供了许多用于数据处理的库和工具,比如SQL、操作、机器学习和图形处理库等。

  4. 高可靠性:Spark具有高可靠性和容错性,如果遇到节点故障,可以自动恢复程序执行。这种容错机制使得Spark在大规模分布式环境中执行任务时非常适用。

总而言之,Spark是一个非常强大而灵活的工具,可以帮助我们解决大数据处理和分析中的许多难题,大大提高处理效率和准确性,也是未来数据处理领域的重要趋势之一。

Spark 计算比 MapReduce 快的根本原因在于 DAG(有向无环图) 计算模型。一般而言,DAG 相比MapReduce 在大多数情况下可以减少shuffle次数。Spark 的 DAGScheduler 相当于一个改进版的 MapReduce,如果计算不涉及与其他节点进行数据交换,Spark可以在内存中一次性完成这些操作,也就是中间结果无须落盘,减少了磁盘IO的操作。但是,如果计算过程中涉及数据交换,Spark 也是会把 shuffle 的数据写磁盘的!
Hadoop和Spark的差异
HadoopSpark
类型基础平台,包含计算,存储,调度分布式计算工具
场景大规模数据集上的批处理迭代计算,交互式计算,流计算
价格对机器要求低,便宜对内存有要求,相对较贵
编程范式MapReduce,API 较为底层,算法适应性差RDD组成DAG有向无环图,API较为顶层,方便使用
数据存储结构MapReduce中间计算结果存在HDFS磁盘上,延迟大RDD中间运算结果存在内存中,延迟小
运行方式Task以进程方式维护,任务启动慢Task以线程方式维护,任务启动快
Spark内置模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWhorACq-1687695909117)(笔记图片.assets\spark-stack.png)]

Spark Core:实现了Spark的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。Spark Core中还包含了对弹性分布式数据集(Resilient Distributed DataSet,简称RDD)的API定义。

Spark SQL:是Spark用来操作结构化数据的程序包。通过Spark SQL,我们可以使用 SQL或者Apache Hive版本的SQL方言(HQL)来查询数据。Spark SQL支持多种数据源,比如Hive表、Parquet以及JSON等。

Spark Streaming:是Spark提供的对实时数据进行流式计算的组件。提供了用来操作数据流的API,并且与Spark Core中的 RDD API高度对应。

Spark MLlib:提供常见的机器学习(ML)功能的程序库。包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据 导入等额外的支持功能。

集群管理器:Spark 设计为可以高效地在一个计算节点到数千个计算节点之间伸缩计 算。为了实现这样的要求,同时获得最大灵活性,Spark支持在各种集群管理器(Cluster Manager)上运行,包括Hadoop YARN、Apache Mesos,以及Spark自带的一个简易调度 器,叫作独立调度器。

2.spark有几种部署方式

1)Local:运行在一台机器上,通常是练手或者测试环境。

2)Standalone:构建一个基于Mster+Slaves的资源调度集群,Spark任务提交给Master运行。是Spark自身的一个调度系统。

3)Yarn: Spark客户端直接连接Yarn,不需要额外构建Spark集群。有yarn-client和yarn-cluster两种模式,主要区别在于:Driver程序的运行节点。

3.什么是RDD

弹性分布式数据集 数据流过而不留下

spark计算的数据存储在RDD中(数据集)

rdd是带有分区的,每个分区存储一部分数据,RDD的分区分布在集群各个节点(分布式)

rdd的分区数在计算中可以增加或者减少 (弹性)

不可变 对rdd中数据的计算会返回一个新的RDD,原有RDD中的数据不会改变

如果RDD的某个分区计算出错了,这个分区的数据会转给其他executor或者其他节点继续执行(分区的容错性)

4.你是如何理解Spark中血统(lineage)的概念

RDD在Lineage依赖方面分为两种Narrow Dependencies与Wide Dependencies用来解决数据容错时的高效性以及划分任务时候起到重要作用

  • lineage 是spark容错的机制的基础,它描述RDD之间的不同依赖关系,记录子RDD是如何从其它RDD中演变过来的,分为宽依赖和窄依赖。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1c386eJH-1687695909118)(笔记图片.assets\1875d7a04e7da3a69668a9a20c944de.png)]

  • 宽依赖:父RDD的分区数据会分发到子RDD的多个分区
  • 窄依赖:父RDD的分区数据会分发到子RDD的一个分区

它的作用是什么?

主要作用是容错。当一个节点的任务失败时,通过血统关系追溯到未发生错误的父RDD,针对父RDD的分区重新计算。

5. Spark的宽窄依赖

RDD的依赖关系

RDD依赖关系也称为RDD的血统,描述了RDD间的转换关系。Spark将RDD间依赖关系分为了宽依赖|ShuffleDependency窄依赖|NarrowDependency,Spark在提交任务的时候会根据转换算子逆向推导出所有的Stage。然后计算推导的stage的分区用于表示该Stage执行的并行度

窄依赖:
  • 父RDD和子RDD partition之间的关系是一对一的。或者父RDD一个partition只对应一个子RDD的partition情况下的父RDD和子RDD partition关系是一对一的。不会有shuffle的产生。父RDD的一个分区去到子RDD的一个分区。
  • 可以理解为独生子女

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMG1tkxU-1687695909119)(笔记图片.assets\0f823c9198beca0c821f749d4d655c3.png)]

宽依赖:
  • 父RDD与子RDD partition之间的关系是一对多。会有shuffle的产生。父RDD的一个分区的数据去到子RDD的不同分区里面。
  • 可以理解为超生

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IoxvUQ7G-1687695909119)(笔记图片.assets\37d5143d6aa9d9f1da2e9ac24f0f762.png)]

6.Spark的reduceByKey和groupByKey的区别

  • reduceByKey和groupByKey都是对键值对进行操作,不同之处在于reduceByKey会在shuffle前对相同key的value进行reduce操作,减少了数据在网络传输中的负载,提高了运行效率;
  • 而groupByKey则会将同一个key的value放到同一个迭代器中,会产生更大量的数据,容易造成内存问题,效率较低。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EKPVuDjg-1687695909120)(笔记图片.assets\904d5dddbc34f46acf3a2e99bcbb463.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cBwAeHH-1687695909128)(笔记图片.assets\7c2a3b9eea4b873abf1f3f3155c7020.png)]

7.说出10个常用的spark算子以及作用

  • map:对数据集的每个元素进行转换,返回一个新的数据集。

  • filter:对数据集进行过滤操作,返回一个新的数据集。

  • flatMap:对数据集中的每个元素进行转换,并返回一个包含所有转换结果的新数据集。

  • reduce:对数据集中的元素进行归约操作,返回一个单独的结果。

  • groupByKey:将数据集中具有相同key的元素分组,并返回一个新的数据集。

  • reduceByKey:对具有相同key的元素进行reduce操作,返回一个新的数据集。

  • join:将具有相同key的元素结合在一起,返回一个新的数据集。

  • sortByKey:根据key对元素排序,返回一个新的数据集。

  • distinct:对数据集中的元素去重,并返回一个新的数据集。

  • count:返回数据集的元素个数。

  • aggregateByKey:根据key对元素进行归约(聚合)操作,返回一个新的数据集。

  • foldByKey:对具有相同key的元素执行reduce操作,与reduceByKey类似,但有一个初始值。

  • cogroup:对两个具有相同key的元素进行合并,返回一个新的数据集。

  • mapPartitions:对数据集中每个分区中的所有元素进行转换,返回一个新的数据集。

  • sample:对数据集进行采样操作,返回一个新数据集。

  • top:返回数据集中的前n个元素。

  • take:返回数据集中的前n个元素。

  • foreach:对数据集中的每个元素执行指定操作,无返回值。

  • union:对两个数据集进行并集操作,返回一个新的数据集。

  • intersection:对两个数据集进行交集操作,返回一个新的数据集。

  • subtract:对两个数据集进行差集操作,返回一个新的数据集

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8BYURLj-1687695909129)(笔记图片.assets\1588608449589.png)]

//1.创建一个RDD
val rdd1: RDD[(String,Int)] = sc.makeRDD(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
//2.取出每个分区相同key对应值的最大值
val rdd2: RDD[(String, Int)] = rdd1.aggregateByKey(0)((v1,v2)=> if(v1 > v2) v1 else v2 ,_+_)
//3.打印
rdd2.collect().foreach(println)
    
结果:(b,3)  (a,3)  (c,12)
行动算子和转换算子

Apache Spark中有两种类型的算子:转换算子(Transformation)和行动算子(Action)。转换算子用于将一个RDD转换为另一个RDD,但不会立即计算。而行动算子则会触发计算,并返回结果给驱动程序或将结果存储到外部系统中。

下面是行动算子和其他算子的区别:

  1. 触发方式:行动算子立即触发数据的处理和计算,而转换算子不会立即触发计算,只有在行动算子要求执行计算时才会触发。
  2. 计算结果:行动算子返回具体的结果值,而转换算子返回一个新的RDD。
  3. 是否产生新的RDD:行动算子不会产生任何新的RDD,但会触发Spark作业计算;而转换算子可以产生新的RDD,并可以链式操作,构建RDD计算流程。
  4. 执行次数:Spark应用程序中可以调用多个转换算子和多个行动算子,但行动算子被调用一次就会形成一个新的job。

需要注意的是,由于行动算子会立即触发Spark计算作业,因此避免在运行过程中对RDD频繁使用行动操作,以免引起不必要的计算开销和资源浪费。同时,对于大数据集的处理,建议采用适当的转换算子操作(如filter、map、flatMap等)进行数据无损压缩,从而减少数据计算、传输和存储的成本。

转换算子(Transformation):
  1. map(func):对RDD中的每个元素应用一个函数。
  2. filter(func):根据给定的条件过滤RDD中的元素。
  3. flatMap(func):对RDD中的每个元素应用一个函数,并把返回的结果展平为一个新的RDD。
  4. mapPartitions(func):对RDD中的每个分区应用一个函数,并把返回的结果组成一个新的RDD。
  5. union(other):返回一个新的RDD,由原始RDD和另一个RDD的所有元素组成。
  6. distinct([numTasks])):返回一个由RDD中唯一的元素组成的新的RDD。
  7. groupByKey():把拥有相同键的元素集合在一起,返回一个元组列表,每个元组包含一个键和对应的值列表。
  8. reduceByKey(func, [numTasks]):对具有相同键的元素分组并聚合。
  9. sortByKey([ascending], [numTasks]):根据键值对RDD中的键进行排序。
  10. join(other, [numTasks]):在RDD的键值对上执行内连接操作。
行动算子(Action):
  1. reduce(func):通过指定的函数聚合RDD中的元素。
  2. collect():返回RDD中所有元素的列表。
  3. count():返回RDD中元素的数量。
  4. first():返回RDD中的第一个元素。
  5. take(n):返回RDD中前n个元素。
  6. saveAsTextFile(path):把RDD中的数据保存到文件。
  7. foreach(func):对RDD中的每个元素应用一个函数。

8.collect算子的用途

collect()的解释

collect()是一个行动算子(Action),在Spark应用程序的执行过程中,它会触发Spark作业的执行,将分布式 RDD(Resilient Distributed Datasets)中的所有数据收集到驱动程序中,并返回一个包含RDD中所有元素的数组。

使用collect()操作时,Spark会将数据从计算节点收集到驱动节点进行汇总。由于collect()是将RDD中的所有数据收集到驱动节点,因此对于大型的RDD来说,调用该操作可能会导致驱动节点的内存溢出或者延迟很大。因此,只有在数据量很小的情况下才应该使用collect()操作。

collect() 是 Spark 中的一种操作,它可以将整个 RDD 的数据收集到驱动器程序中。这个操作需要将整个 RDD 的数据都加载到内存中,因而只推荐对小数据集使用。一般情况下,collect() 应该是 RDD 上的最后一个操作,因为一旦调用,整个 RDD 都会被装入内存,而且可能会消耗大量内存。

总之,collect() 可以帮助我们在 PySpark 中查看整个 RDD 的全部数据内容,但需要谨慎使用,因为它对大型数据集的执行效率比较低,可能会引发内存溢出问题。

使用collect()内存溢出问题如何解决?

如果只是想查看 RDD 的一部分数据,可以使用 take() 或者 first() 等操作。这些操作可以从 RDD 的所有分区中返回一定数量的元素到驱动器节点中。

如果需要写出 RDD中的全部数据,可以使用 saveAsTextFile() 或者 saveAsSequenceFile() 等操作将RDD保存到分布式存储系统(比如HDFS)中。保存到分布式存储系统中,可以方便地将数据文件进行存储和管理,也可以方便地读取到其他的大数据处理系统中做进一步的处理。

除此之外,还可以使用 Spark SQL 进行数据分析处理。Spark SQL 提供了类似于传统的 SQL 查询方式的 API,通过查询 RDD 中的数据,可以方便地进行数据抽取、转换和过滤等操作。同时,Spark SQL 还支持多种数据源的接入,如 Hive、JSON、Parquet等格式,可以方便地处理多样化的数据。

9.foreach和foreachPartition的区别?

foreach()foreachPartition() 都是 Spark 中的行动(Action)操作,它们接收一个函数作为参数,该函数将 RDD 中的每个元素进行处理。

主要区别:
  1. foreach() 主要应用在元素级别的操作中,将 RDD 中的每个元素都传输到driver端,这样可能导致网络开销和缓存同步的开销很高。
  2. foreachPartition() 主要应用在分区级别的操作中,它可以将每个分区都传输给一个单独的迭代器(iterator)进行处理,因此可以避免每个元素单独传输到driver端的问题,降低了网络开销和缓存的时间开销。

举个例子,假设有一个 RDD,存储了若干个数字,我们想对每个数字都乘以2,然后打印出来。

使用 foreach() 可以这样写:

rdd.foreach(lambda x: print(x * 2))

这样每个元素都会逐个传输到驱动程序,进行乘以2,然后才会执行打印。

而使用 foreachPartition() 可以这样写:

def f(iterator):
    for x in iterator:
        print(x * 2)

rdd.foreachPartition(f)

这样会先将RDD分区,然后对每个分区进行操作。对于一个分区内的所有元素,利用迭代器一次性获取,进行一次性操作,从而避免每个元素的开销,大大提高了计算效率。

总的来说, foreach()foreachPartition() 都能完成对RDD中数据的迭代处理,但是 foreachPartition() 可以避免在处理每个元素时都需要转移大量数据到驱动器的开销,对于大规模数据处理来说更加高效。

自我总结:

我认为这些了理论难以表述清楚,举个场景:

在Spark中,foreach()操作会对RDD的每个元素执行一次函数。如果这个函数需要使用到数据库连接,那么每个元素执行时都需要进行一次数据库连接,这将导致多次的数据库连接和断开连接操作,造成性能浪费。如果改用foreachPartition就会根据分区数来连接数据库,一般分区数远远小于元素个数,这样的话就会大大减少连接的次数。

10.RDD和DataFrame和DataSet三者间的区别

在开始Spark RDD与DataFrame与Dataset之间的比较之前,先让我们看一下Spark中的RDD,DataFrame和Datasets的定义:

Spark RDD:RDD代表弹性分布式数据集。它是记录的只读分区集合。 RDD是Spark的基本数据结构。它允许程序员以容错方式在大型集群上执行内存计算。

Spark Dataframe:与RDD不同,数据以列的形式组织起来,类似于关系数据库中的表。它是一个不可变的分布式数据集合。 Spark中的DataFrame允许开发人员将数据结构(类型)加到分布式数据集合上,从而实现更高级别的抽象。

Spark Dataset:Apache Spark中的Dataset是DataFrame API的扩展,它提供了类型安全(type-safe),面向对象(object-oriented)的编程接口。 Dataset利用Catalyst optimizer可以让用户通过类似于sql的表达式对数据进行查询。

细说DataFrame
DataFrame的前身是SchemaRDD。Spark1.3更名为DataFrame。不继承RDD,自己实现了RDD的大部分功能。

与RDD类似,DataFrame也是一个分布式数据集:
1)DataFrame可以看做分布式 Row 对象的集合,提供了由列组成的详细模式信息,使其可以得到优化。DataFrame 不仅有比RDD更多的算子,还可以进行执行计划的优化。
2)DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。
3)DataFrame也支持嵌套数据类型(struct、array和map)。
4)DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。
5)Dataframe的劣势在于在编译期缺少类型安全检查,导致运行时出错。

细说DataSet

1)DataSet是在Spark1.6中添加的新的接口。
2)与RDD相比,保存了更多的描述信息,概念上等同于关系型数据库中的二维表。
3)与DataFrame相比,保存了类型信息,是强类型的,提供了编译时类型检查。
4)调用Dataset的方法先会生成逻辑计划,然后Spark的优化器进行优化,最终生成物理计划,然后提交到集群中运行。
5)DataSet包含了DataFrame的功能,在Spark2.0中两者得到了统一:DataFrame表示为DataSet[Row],即DataSet的子集。

结构图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaOlUCPq-1687695909129)(笔记图片.assets\ae7bb54ae360467ef3a395e05ef2905.png)]

1)RDD[Person]:
以Person 为类型参数,但不了解其内部结构。
2)DataFrame:
提供了详细的结构信息schema 列的名称和类型。这样看起来就像一张表了。
3)DataSet:
不光有schema 信息,还有类型信息。

数据图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dlG9Vk2k-1687695909130)(笔记图片.assets\53c5f9563cc009324fbbc2637e07bdc.png)]

在DataFrame中,可以使用row对象的属性或者方法来获取每个元素的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BpElboqD-1687695909130)(笔记图片.assets\13bc2599cf1c8611b41c12e86947a32.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qh48pUio-1687695909131)(笔记图片.assets\c004d39999617a15c702df3f0a5a0c5.png)]

RDD、DataFrame和DataSet的联系
RDD

优点:

编译时类型安全
编译时就能检查出类型错误
面向对象的编程风格
直接通过类名点的方式来操作数据

缺点:

序列化和反序列化的性能开销
无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化
GC的性能开销
频繁的创建和销毁对象, 势必会增加GC
DataFrame

DataFrame引入了schema和off-heap:

schema : RDD每一行的数据, 结构都是一样的。这个结构就存储在schema中。 Spark通过schame就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了。

off-heap : 意味着JVM堆以外的内存, 这些内存直接受操作系统管理(而不是JVM)。Spark能够以二进制的形式序列化数据(不包括结构)到off-heap中, 当要操作数据时, 就直接操作off-heap内存。由于Spark理解schema, 所以知道该如何操作。

    off-heap就像地盘, schema就像地图, Spark有地图又有自己地盘了, 就可以自己说了算了, 不再受JVM的限制, 也就不再收GC的困扰了。

    通过schema和off-heap, DataFrame解决了RDD的缺点, 但是却丢了RDD的优点。 DataFrame不是类型安全的, API也不是面向对象风格的。

    DataFrame也可以叫Dataset[Row],每一行的类型是Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的getAS方法或者共性中的第七条提到的模式匹配拿出特定字段。

优点:

    DataFrame 内部有明确 Scheme 结构,即列名、列字段类型都是已知的,这带来的好处是可以减少数据读取以及更好地优化执行计划,从而保证查询效率。 	

缺点:

(1)Dataframe的劣势在于在编译期缺少类型安全检查,导致运行时出错。

(2)DataFrame虽然是结构化的,但是其所含的值并没有对应一个class,所以spark就定义了一个class名为Row,作为DataFrame的数据的数据结构。所以DataFrame等价于Dataset[Row]。但是Row又没有定义field,具体包含哪些字段,没法直接取出来,所以只能通过Row的各种方法比如getAsInt来获取属性xxx的内容。而Dataset每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获得每一行的信息。所以DataFrame在获取内部数据的时候,方法数据的属性没有Dataset方便。

DataSet
DataSet结合了RDD和DataFrame的优点, 并带来的一个新的概念Encoder。

当序列化数据时, Encoder产生字节码与off-heap进行交互, 能够达到按需访问数据的效果, 而不用反序列化整个对象。
三者的互相转化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXRSllSs-1687695909131)(笔记图片.assets\f72c2c981fe0f250bdc7efc78f07de9.png)]

11.spark提交作业的参数

提交任务时的几个重要参数

executor-cores——每个executor使用的内核数,默认为1,官方建议为2-5个。
num-executors——启动executor的数量,默认为2。
executor-memory——executor内存大小默认1G。
driver-cores——driver使用内核数,默认一个。
driver-memory——driver内存大小,默认512M。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QSrgsJT-1687695909131)(笔记图片.assets\4ddcd95bf7dc3ab54f722d1512866a3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8ps4ylX-1687695909132)(笔记图片.assets\3309c86c458e7ced18011e07a43f3bd.png)]

12.Application、job、Stage、task之间的关系

一个应用程序对应多个job,一个job会有多个stage阶段,一个stage会有多个task

2、一个应用程序中有多少个行动算子就会创建多少个job作业;一个job作业中一个宽依赖会划分一个stage阶段;同一个stage阶段中最后一个算子有多少个分区这个stage就有多少个task,因为窄依赖每个分区任务是并行执行的,没有必要每个算子的一个分区启动一个task任务。如图所示阶段2最后一个map算子是对应5个分区,reducebykey是3个分区,总共是8个task任务。

3、当一个rdd的数据需要打乱重组然后分配到下一个rdd时就产生shuffle阶段,宽依赖就是以shuffle进行划分的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QnfCamL-1687695909132)(笔记图片.assets\7379f2017d5f83db386af10e5e017b0.png)]

Application是一个Spark程序,可能包含一个或多个Spark作业。一个Spark Application通常包含一个Driver程序和多个Executor程序。Job是Spark中提交给Scheduler的计算单元,可以看作由一系列相关的RDD操作组成的有向无环图(DAG),按照依赖关系进行执行。Stage是Job中的一个分段,它是一组可以并行处理的Task的集合,属于同一阶段的Task之间没有依赖关系。Ta sk是Spark中的最小执行单元,它实际上是一个将RDD的分片映射到计算节点上的处理过程。

13.简述Spark中共享变量(广播变量和累加器)的基本原理与用途

累加器是spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数;

广播变量是每个机器上缓存一份,不可变,只读的,相同的变量,该节点每个任务都能访问,起到节省资源和优化的作用。它通常用来高效分发较大的对象。

一、共享变量的工作原理

Spark还有一个非常重要的特性就是共享变量。默认情况下,如果在一个算子函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个task中。此时每个task只能操作自己的那份变量数据。如果多个task想要共享某个变量,那么这种方式是做不到的

Spark为此提供了两种共享变量:
(1)Broadcast Variable(广播变量)
(2)Accumulator(累加变量)
二、广播变量
原理:

广播变量:实际上就是Executor端用到了driver端的变量

如果在executor端你使用到了driver端的广播变量,如果不使用广播变量,在每个executor中有多少task就有多少变量副本。

注意事项:
Broadcast Variable会将使用到的变量,仅仅为每个节点拷贝一份,而不是每个task,能够优化性能,减少网络传输及内存消耗
通过SparkContext的broadcast()方法,针对某个变量创建广播变量,可以通过广播变量的value()方法获取值
广播变量是只读的
不能将RDD广播出去,RDD不存数据,可以将RDD的结果广播出去,rdd.collect()
广播变量只能在Driver定义,在Executor端不能修改广播变量的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EEgPYMhi-1687695909132)(笔记图片.assets\9414182dc36ae1aab0f443b8d90bbd3.png)]

大家可以想象一个极端情况,如果map算子有10个task,恰好这10个task还都在一个worker节点上,那么这个时候,map算子使用的外部变量就会在这个worker节点上保存10份,这样就很占用内存了。

1. Scala代码
import org.apache.spark.{SparkConf, SparkContext}

object BroadcastOpScala {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setAppName("BroadcastOpScala").setMaster("local[*]")
    val sc = new SparkContext(conf)

val rdd = sc.parallelize(Array(1, 2, 3, 4, 5))
val varable = 2
//    rdd.map(_ * varable).foreach(println(_))
// 1.定义广播变量
val varableBroad = sc.broadcast(varable)
// 2.使用广播变量,调用value方法
rdd.map(_ * varableBroad.value).foreach(println(_))
sc.stop()
  }
}
2. Java代码
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.broadcast.Broadcast;

import java.util.Arrays;
import java.util.List;

public class BroadcastOpJava {
    public static void main(String[] args) {
        SparkConf conf = new SparkConf();
        conf.setAppName("BroadcastOpJava").setMaster("local[*]");
        JavaSparkContext sc = new JavaSparkContext(conf);

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
JavaRDD<Integer> rdd = sc.parallelize(list);

int varable = 2;
// 1.定义广播变量
Broadcast<Integer> varableBroad = sc.broadcast(varable);

// 2.使用广播变量
rdd.map(new Function<Integer, Integer>() {
    @Override
    public Integer call(Integer v1) throws Exception {
        return v1 * varableBroad.value();
    }
}).foreach(new VoidFunction<Integer>() {
    @Override
    public void call(Integer integer) throws Exception {
        System.out.println(integer);
    }
});

sc.stop();
   }
}
三、累加变量

Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作。正常情况下在Spark的任务中,由于一个算子可能会产生多个task并行执行,所以在这个算子内部执行的聚合计算都是局部的,想要实现多个task进行全局聚合计算,此时需要使用到Accumulator这个共享的累加变量。

注意:Accumulator只提供了累加的功能。在task只能对Accumulator进行累加操作,不能读取它的值。只有在Driver进程中才可以读取Accumulator的值。

14.spark中rdd的缓存机制

Spark RDD缓存机制是指在内存中缓存RDD分区数据,以便于重复操作时将数据快速获取,提高运行效率的一种技术。

当我们把一个RDD进行缓存后,它所代表的数据就可以被保存在集群中,其他RDD计算结果需要利用缓存的RDD的数据时,可以避免多次重复计算的问题,从而提高计算速度。

具体地,Spark 的 RDD 缓存是通过 Cache() 和 Persist() 两个方法来实现的。

Cache() 方法会将 RDD 的分区数据存储在内存中,如果内存不足,还可以将 RDD 的数据写入磁盘;而 Persist() 方法则允许用户指定 RDD 存储的级别,包括 MEMORY_ONLY、MEMORY_ONLY_SER、MEMORY_AND_DISK、MEMORY_AND_DISK_SER、DISK_ONLY 等级别。

其中,MEMORY_ONLY 级别会将 RDD 的分区数据缓存到内存中,而 MEMORY_ONLY_SER 级别则会将序列化后的 RDD 分区数据缓存到内存中,MEMORY_AND_DISK 级别会将 RDD 分区数据先缓存到内存中,如果内存空间不足,则会将部分数据进行序列化后暂存在磁盘上,MEMORY_AND_DISK_SER 则是将序列化后的数据缓存到内存中,超出内存空间的数据放在磁盘上,DISK_ONLY 级别则是将数据直接写入磁盘中。

使用缓存时需要注意以下事项:
  1. RDD 的缓存是针对分区级别的,而不是 RDD 本身。
  2. 使用缓存时需要分析系统内存和磁盘的大小和使用情况,正确设置缓存级别。
  3. 缓存也会消耗内存资源,过多缓存会使得资源不够,从而导致内存溢出。
  4. 缓存计算量大的 RDD 可以提高下一步RDD计算的性能,但缓存的 RDD 的数据被更新时需刷新 RDD。

总的来说,使用缓存机制可以极大的提升Spark应用程序的运行速度,因此在开发Spark应用时,应尽量使用缓存机制,以提升程序性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TrYU1hjd-1687695909132)(笔记图片.assets\36a60fea7c2e94cc9c6b45936237657.png)]

15.简述下Spark中的缓存(cache和persist)与checkpoint机制,并指出两者的区别和联系

都是做RDD持久化的

cache:内存,不会截断血缘关系,使用计算过程中的数据缓存。

checkpoint:磁盘,截断血缘关系,在ck之前必须没有任何任务提交才会生效,ck过程会额外提交一次任务。

对于作业中的某些RDD,如果其计算代价大,之后会被多次用到,则可以考虑将其缓存,再次用到时直接使用缓存,无需重新计算。

是一种运行时性能优化方案。

checkpoint是将某些关键RDD的计算结果持久化到文件系统,当task错误恢复时,调度系统会从checkpoint过的RDD开始计算,而不会从头计算。

二者区别和联系:

缓存是临时性的,如果发生错误,重新恢复后缓存失效,而且在使用结束后,应该主动清楚缓存;

checkpoint会将RDD相关数据和信息存储到文件系统,错误恢复时可以从文件系统将RDD重新恢复,checkpoint不需要主动清除;

缓存不会截断DAG,checkpoint会截断DAG;

由于checkpoint会触发重新计算,故可以在checkpoint前将对应的RDD缓存;

缓存和checkpoint都不是action,需要action触发才能执行真正缓存和checkpoint,,如下:

 val rdd1 = rdd.cache()
    rdd1.checkpoint()
    rdd1.count()

16.Spark常见数据倾斜情况及调优方案

参考网址: https://www.modb.pro/db/627315

数据倾斜

Spark中的数据倾斜问题主要指shuffle过程中出现的数据倾斜问题,是由于不同的key对应的数据量不同导致的不同task所处理的数据量不同的问题

例如,reduce点一共要处理100万条数据,第一个和第二个task分别被分配到了1万条数据,计算5分钟内完成,第三个task分配到了98万数据,此时第三个task可能需要10个小时完成,这使得整个Spark作业需要10个小时才能运行完成,这就是数据倾斜所带来的后果

数据倾斜俩大直接致命后果

1)数据倾斜直接会导致一种情况:Out Of Memory

2)运行速度慢

注意,要区分开数据倾斜与数据量过量这两种情况,数据倾斜是指少数task被分配了绝大多数的数据,因此少数task运行缓慢;数据过量是指所有task被分配的数据量都很大,相差不多,所有task都运行缓慢

数据倾斜的表现:

1)Spark作业的大部分task都执行迅速,只有有限的几个task执行的非常慢,此时可能出现了数据倾斜,作业可以运行,但是运行得非常慢

2)Spark作业的大部分task都执行迅速,但是有的task在运行过程中会突然报出OOM,反复执行几次都在某一个task报出OOM错误,此时可能出现了数据倾斜,作业无法正常运行

定位数据倾斜问题:

1)查阅代码中的shuffle算子,例如reduceByKey、countByKey、groupByKey、join等算子,根据代码逻辑判断此处是否会出现数据倾斜

2)查看Spark作业的log文件,log文件对于错误的记录会精确到代码的某一行,可以根据异常定位到的代码位置来明确错误发生在第几个stage,对应的shuffle算子是哪一个

解决方案一:聚合元数据

1)避免shuffle过程

绝大多数情况下,Spark作业的数据来源都是Hive表,这些Hive表基本都是经过ETL之后的昨天的数据

为了避免数据倾斜,我们可以考虑避免shuffle过程,如果避免了shuffle过程,那么从根本上就消除了发生数据倾斜问题的可能

如果Spark作业的数据来源于Hive表,那么可以先在Hive表中对数据进行聚合,例如按照key进行分组,将同一key对应的所有value用一种特殊的格式拼接到一个字符串里去,这样,一个key就只有一条数据了;之后,对一个key的所有value进行处理时,只需要进行map操作即可,无需再进行任何的shuffle操作。通过上述方式就避免了执行shuffle操作,也就不可能会发生任何的数据倾斜问题。

对于Hive表中数据的操作,不一定是拼接成一个字符串,也可以是直接对key的每一条数据进行累计计算

要区分开,处理的数据量大和数据倾斜的区别

2)缩小key粒度(增大数据倾斜可能性,降低每个task的数据量)

key的数量增加,可能使数据倾斜更严重

3)增大key粒度(减小数据倾斜可能性,增大每个task的数据量)

如果没有办法对每个key聚合出来一条数据,在特定场景下,可以考虑扩大key的聚合粒度

例如,目前有10万条用户数据,当前key的粒度是(省,城市,区,日期),现在我们考虑扩大粒度,将key的粒度扩大为(省,城市,日期),这样的话,key的数量会减少,key之间的数据量差异也有可能会减少,由此可以减轻数据倾斜的现象和问题。(此方法只针对特定类型的数据有效,当应用场景不适宜时,会加重数据倾斜)

解决方案二:过滤导致倾斜的key

如果在Spark作业中允许丢弃某些数据,那么可以考虑将可能导致数据倾斜的key进行过滤,滤除可能导致数据倾斜的key对应的数据,这样,在Spark作业中就不会发生数据倾斜了

解决方案三:提高shuffle操作中的reduce并行度

当方案一和方案二对于数据倾斜的处理没有很好的效果时,可以考虑提高shuffle过程中的reduce端并行度,reduce端并行度的提高就增加了reduce端task的数量,那么每个task分配到的数据量就会相应减少,由此缓解数据倾斜问题

1)reduce端并行度的设置

在大部分的shuffle算子中,都可以传入一个并行度的设置参数,比如reduceByKey(500),这个参数会决定shuffle过程中reduce端的并行度,在进行shuffle操作的时候,就会对应着创建指定数量的reduce task。对于Spark SQL中的shuffle类语句,比如group by、join等,需要设置一个参数,即spark.sql.shuffle.partitions,该参数代表了shuffle read task的并行度,该值默认是200,对于很多场景来说都有点过小

增加shuffle read task的数量,可以让原本分配给一个task的多个key分配给多个task,从而让每个task处理比原来更少的数据。举例来说,如果原本有5个key,每个key对应10条数据,这5个key都是分配给一个task的,那么这个task就要处理50条数据。而增加了shuffle read task以后,每个task就分配到一个key,即每个task就处理10条数据,那么自然每个task的执行时间都会变短了

2)reduce端并行度设置存在的缺陷

提高reduce端并行度并没有从根本上改变数据倾斜的本质和问题(方案一和方案二从根本上避免了数据倾斜的发生),只是尽可能地去缓解和减轻shuffle reduce task的数据压力,以及数据倾斜的问题,适用于有较多key对应的数据量都比较大的情况

该方案通常无法彻底解决数据倾斜,因为如果出现一些极端情况,比如某个key对应的数据量有100万,那么无论你的task数量增加到多少,这个对应着100万数据的key肯定还是会分配到一个task中去处理,因此注定还是会发生数据倾斜的。所以这种方案只能说是在发现数据倾斜时尝试使用的第一种手段,尝试去用嘴简单的方法缓解数据倾斜而已,或者是和其他方案结合起来使用

在理想情况下,reduce端并行度提升后,会在一定程度上减轻数据倾斜的问题,甚至基本消除数据倾斜;但是,在一些情况下,只会让原来由于数据倾斜而运行缓慢的task运行速度稍有提升,或者避免了某些task的OOM问题,但是,仍然运行缓慢,此时,要及时放弃方案三,开始尝试后面的方案

解决方案四:使用随机key实现双重聚合 (常用的)

首先,通过map算子给每个数据的key添加随机数前缀,对key进行打散,将原先一样的key变成不一样的key,然后进行第一次聚合,这样就可以让原本被一个task处理的数据分散到多个task上去做局部聚合;随后,去除掉每个key的前缀,再次进行聚合

此方法对于由groupByKey、reduceByKey这类算子造成的数据倾斜由比较好的效果,仅仅适用于聚合类的shuffle操作,适用范围相对较窄。如果是join类的shuffle操作,还得用其他的解决方案

此方法也是前几种方案没有比较好的效果时要尝试的解决方案

17.Spark任务执行流程

1. Spark作业运行流程

① 构建Spark Application的运行环境(启动SparkContext),SparkContext向资源管理器(YARN)注册并申请运行Executor资源;

② 资源管理器分配并启动Executor,Executor的运行情况将随着心跳发送到资源管理器上;

③ SparkContext构成DAG图,将DAG图分解成Stage(Taskset),并把Taskset发送给Task Scheduler。Executor向SparkContext申请Task;

④ Task Scheduler将Task发放给Executor运行,同时SparkContext将应用程序代码发放给Executor;

⑤ Task在Executor上运行,运行完毕释放所有资源。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNhNuK8Z-1687695909133)(笔记图片.assets\ae3fdd52842ba5057fe6fd09125325c.png)]

2. 任务提交四个阶段

① 构建DAG
用户提交的job将首先被转换成一系列RDD,并通过RDD之间的依赖关系构建DAG,然后将DAG提交到调度系统;

② DAG调度
DagSucheduler将DAG切分stage(切分依据是shuffle),将stage中生成的task以taskset的形式发送给TaskScheduler;

③ TaskScheduler调度Task(根据资源情况将task调度到Executors);

④ Executors接收task,然后将task交给线程池执行。

3.Spark运行原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Y3gye0h-1687695909133)(笔记图片.assets\99d854a8b4aa076ef79452e22216250.png)]

Spark应用程序以**“进程集合”**为单位在分布式集群上运行,通过driver程序的main方法创建的SparkContext对象与集群交互的;

① Spark通过SparkContext向Cluster manager(资源管理器)申请所需执行的资源(cpu、内存等);

② Cluster manager分配应用程序执行需要的资源,在Worker节点上创建Executor;

③ SparkContext将程序代码(jar包或python文件)和Task任务发送给Executor执行,并收集结果给Driver。

相关组件功能:

​ master: 管理集群和节点,不参与计算;

​ worker: 计算节点,进程本身不参与计算,和 master 汇报;

​ Driver: 运行程序的 main 方法,创建 spark context 对象;

​ spark context: 控制整个 application 的生命周期,包括 dagsheduler 和 task scheduler 等组件;

​ client: 用户提交程序的入口。

18.SparkSQl简介及运行原理

Spark SQL就是将SQL转换成一个任务,提交到集群上运行,类似于Hive的执行方式。今天通过本文给大家分享SparkSQl简介及运行原理,感兴趣的朋友跟随小编一起看看吧

什么是SparkSQL?
(一)SparkSQL简介

Spark SQL是Spark的一个模块,用于处理结构化的数据,它提供了一个数据抽象DataFrame(最核心的编程抽象就是DataFrame),并且SparkSQL作为分布式SQL查询引擎。
Spark SQL就是将SQL转换成一个任务,提交到集群上运行,类似于Hive的执行方式。

(二)SparkSQL运行原理

将Spark SQL转化为RDD,然后提交到集群执行。

使用SessionCatalog保存元数据:
在解析SQL语句之前,会创建SparkSession,或者如果是2.0之前的版本初始化SQLContext,SparkSession只是封装了SparkContext和SQLContext的创建而已。会把元数据保存在SessionCatalog中,涉及到表名,字段名称和字段类型。创建临时表或者视图,其实就会往SessionCatalog注册。

1)SqlParse: 基于antlr框架对 sql解析,生成抽象语法树;

当调用SparkSession的sql或者SQLContext的sql方法,我们以2.0为准,就会使用SparkSqlParser进行解析SQL. 使用的ANTLR进行词法解析和语法解析。它分为2个步骤来生成Unresolved LogicalPlan:
词法分析:Lexical Analysis,负责将token分组成符号类
构建一个分析树或者语法树AST

2)Analyzer: 主要完成绑定工作,将不同来源的Unresolved LogicalPlan和元数据(如hive metastore、Schema catalog)进行绑定,生成resolved LogicalPlan;

在该阶段,Analyzer会使用Analyzer Rules,并结合SessionCatalog,对未绑定的逻辑计划进行解析,生成已绑定的逻辑计划。

3)optimizer: 对resolvedLogicalPlan进行优化,生成optimizedLogicalPlan(OptimizationRules,对resolvedLogicalPlan进行合并、列裁剪、过滤器下推等优化作业而转换成optimized LogicalPlan);

优化器也是会定义一套Rules,利用这些Rule对逻辑计划和Exepression进行迭代处理,从而使得树的节点进行合并和优化。

4)Planner: 将LogicalPlan转换成PhysicalPlan;

SparkSpanner使用Planning Strategies,对优化后的逻辑计划进行转换,生成可以执行的物理计划SparkPlan.

5)CostModel: 主要根据过去的性能统计数据,选择最佳的物理执行计划。

此时调用SparkPlan的execute方法,底层其实已经再触发JOB了,然后返回RDD。

(三)SparkSQL特点

(1)容易整合,Spark SQL已经集成在Spark中

(2)提供了统一的数据访问方式:JSON、CSV、JDBC、Parquet等都是使用统一的方式进行访问

(3)兼容 Hive

(4)标准的数据连接:JDBC、ODBC

19.spark中写的sql怎么连接hive,mysql

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27MQv2cJ-1687695909133)(笔记图片.assets\0d167a943410a78f1f5507dff7e1599.png)]

新版api
  • package com.dkl.leanring.spark.sql
    

 import org.apache.spark.sql.SparkSession
   
​```scala
/**
* 新版本spark-hive测试
*/
  object NewSparkHiveDemo {
    def main(args: Array[String]): Unit = {
val spark = SparkSession
    .builder()
    .appName("Spark Hive Example")
    .master("local")
    .config("spark.sql.warehouse.dir", "/user/hive/warehouse/")
    .enableHiveSupport()           
    .getOrCreate()

  import spark.implicits._   
import spark.sql
sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
val data = Array((1, "val1"), (2, "val2"), (3, "val3"))
var df = spark.createDataFrame(data).toDF("key", "value")
df.createOrReplaceTempView("temp_src")
sql("insert into src select key,value from temp_src")
sql("SELECT * FROM src").show()
   }
}
-- 直接用下面这句代码即可将df里的全部数据存到hive表里
df.write.mode(SaveMode.Append).saveAsTable(tableName)
写入MySQL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNf8Ityu-1687695909133)(笔记图片.assets\5b760755f99af4d4a2d175312ccb142.png)]

df3.write.format("jdbc").option("url", "jdbc:mysql://hadoop10:3306/wzy?useSSL=false&characterEncoding=utf8")
 .option("user", "root")
 .option("password", "123456")
 .option("driver", "com.mysql.jdbc.Driver")
 .option("dbtable", "visit")
 .mode("append")
 .save
maven依赖
<dependencies>
    <!-- spark依赖-->
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.4.3</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>

    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.1.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-auth</artifactId>
        <version>3.1.4</version>
    </dependency>

    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-server</artifactId>
        <version>1.5.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-client</artifactId>
        <version>1.5.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.11</artifactId>
        <version>2.4.3</version>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.1.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
       <artifactId>spark-hive_2.11</artifactId>
        <version>2.4.3</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <!-- maven项目对scala编译打包 -->
        <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>scala-maven-plugin</artifactId>
            <version>4.0.1</version>
            <executions>
                <execution>
                    <id>scala-compile-first</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>add-source</goal>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

20.Spark 与 MapReduce 的 Shuffle 的区别

Shuffle 是一种 Spark 和 MapReduce 执行过程中进行数据重分区(Reshuffle)的过程。虽然 Spark 和 MapReduce 的 Shuffle 都有类似的目的,但在实现方式和效率上有所不同。

实现方式

MapReduce 的 Shuffle 由 Map 和 Reduce 两个阶段组成。在 Map 阶段,文件被分成 RecordReader 读入其中一部分,然后被切割成片并均匀地分配到紧随其后的 Reduce 阶段的节点中。在 Reduce 阶段,记录通过 Partitioner 被分组,然后进行排序和聚合。

Spark 的 Shuffle 与 MapReduce 的 Shuffle 类似,但是除了提供默认的 HashPartitioner 之外,它还提供了 RangePartitioner、CoalescedPartitioner 等自定义的分区器。Spark 也提供了 Broadcast、ExternalSort 和 Tungsten 等不同的文件排序器,以优化 Shuffle 的性能。

效率

Spark 的 Shuffle 比 MapReduce 的 Shuffle 效率更高。这是因为 Spark 提供了基于内存排序和计算、基于磁盘的排序和跨网络的排序等不同类型的排序器,可以在 Shuffle 过程中使用最佳的器件,以提高排序和聚合的速度。同时,Spark 使用了一个更有效的内存管理技术,可以处理高速运行过程中产生的大量数据。

总体而言,Spark 的 Shuffle 效率更高,这得益于 Spark 借鉴了 Hadoop MapReduce 的经验,并进一步优化了算法和技术。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值