spark面试总结

Spark 是一个用于大规模数据处理的开源分布式计算系统。它提供了高效的数据处理能力,支持多种数据处理场景,包括批处理、流处理、机器学习和图计算等。最近的面试中面试官问的比较多,特地总结一些

1.Spark 运行时各个组件及作用

  • 集群管理器(Cluster Manager):Spark 运行时需要一个集群管理器来管理计算资源。常见的集群管理器包括 Apache Mesos、Hadoop YARN 和 Spark 自带的 Standalone 模式。

  • 资源调度(Resource Scheduling):一旦连接到集群管理器,Spark 就需要一个资源调度器来分配计算资源给不同的 Spark 应用程序。资源调度器决定哪些任务运行在哪些节点上,并确保任务的执行不会受到资源竞争的影响。

  • 执行引擎(Execution Engine):Spark 使用 DAG(Directed Acyclic Graph)执行引擎执行任务。Spark 应用程序被转换成一个由一系列阶段(Stages)组成的 DAG,每个阶段包含一组可以并行执行的任务。Spark 支持两种类型的操作:转换(Transformations)和行动(Actions)。转换会生成新的数据集,而行动会触发作业的执行。

  • 内存管理(Memory Management):Spark 运行时使用内存来加速数据处理。它使用内存来缓存中间数据,并尽可能地减少磁盘 I/O。Spark 提供了多种内存管理策略,包括静态分配和动态分配,以及可调整的内存分配。

  • 任务调度(Task Scheduling):一旦 DAG 被构建,Spark 将任务调度到集群中的工作节点上执行。任务调度器负责将任务分配给可用的计算资源,并确保任务的并行执行。

  • 数据分区(Data Partitioning):在 Spark 中,数据通常被分成多个分区,每个分区可以在集群中的不同节点上并行处理。数据分区的良好设计可以提高任务的并行度和性能。

  • 容错性(Fault Tolerance):Spark 提供了容错机制,使得在节点失败时能够恢复计算,并且不会丢失数据。这通过RDD(Resilient Distributed Dataset)的分布式数据集实现,以及RDD的血统(Lineage)记录和任务重新执行来实现。

2.Spark 运行原理主要涉及以下几个方面

  • 分布式数据集(Resilient Distributed Dataset,RDD):Spark 的核心数据抽象是 RDD,它代表一个可以并行操作的、可分区的数据集合。RDD 具有容错性,因此可以在节点故障时进行恢复。Spark 中的所有计算都是围绕 RDD 进行的。

  • 惰性求值(Lazy Evaluation):Spark 中的转换操作是惰性的,即当应用一个转换操作时,它并不立即执行,而是构建一个执行计划(DAG),只有当遇到行动操作时,才会触发实际的计算。

  • 行动操作(Actions):行动操作是对 RDD 执行实际计算并返回结果的操作,例如 collect、count、saveAsTextFile 等。当执行行动操作时,Spark 才会根据之前构建的执行计划执行转换操作,并将结果返回给驱动程序。

  • 转换操作(Transformations):转换操作是对 RDD 进行变换的操作,例如 map、filter、reduceByKey 等。这些操作会生成一个新的 RDD,而不改变原始的 RDD。

  • 任务调度(Task Scheduling):Spark 会将转换操作划分为一系列的任务,并将这些任务分发给集群中的各个节点执行。任务调度器负责将任务分配给可用资源,并优化任务执行顺序以提高性能。

  • 内存计算(In-Memory Computation):Spark 充分利用内存进行计算,通过将数据存储在内存中而不是磁盘上,可以大大提高计算速度。此外,Spark 还提供了基于内存的缓存机制,可以在多个计算之间共享中间结果,减少重复计算。

  • 容错性(Fault Tolerance):Spark 通过将计算过程中的中间结果存储在可恢复的 RDD 中实现容错性。如果某个节点失败,Spark 可以重新计算丢失的部分,而无需重新开始整个计算过程。

总的来说,Spark 的运行原理是基于RDD的惰性求值和转换操作,配合任务调度和内存计算,实现了高效的大规模数据处理

3.Spark任务执行步骤

  • 应用提交阶段

    • 用户编写Spark应用程序代码,并将其提交给Spark集群。
    • 在应用提交时,Spark Driver即驱动程序负责解析应用程序代码、创建应用程序的DAG图(有向无环图)。
  • 作业划分阶段

    • DAGScheduler将整个DAG图划分为多个Stage,每个Stage由若干个Task组成,按照RDD之间的依赖关系进行划分。
    • Stage划分完成后,将根据每个Stage的Task计算量和数据依赖关系,确定各个Stage的调度顺序。
  • 任务调度阶段

    • DAGScheduler将各个Stage中的Task发送给TaskScheduler进行实际的任务调度和分配。
    • TaskScheduler将Task分发给Executor(工作节点)进行执行,任务可以在同一节点内执行,也可以跨节点执行。
  • 资源管理阶段

    • ResourceManager(如YARN或Mesos)负责对集群资源进行管理和分配,为Executor分配所需的计算资源和内存资源。
    • Executor启动后,将为任务执行提供计算资源和内存。
  • 任务执行阶段

    • 每个Executor会启动一个或多个Task,执行具体的Spark作业计算。
    • 任务执行完成后,将结果返回给Driver端。
  • 作业完成阶段

    • 当所有的Task执行完成后,Spark应用程序执行完毕。
    • 应用程序会输出结果,或将结果写入外部存储。
  • 清理阶段

    • 在作业完成后,Spark会清理资源,包括释放Executor的资源和清理中间数据。
  • 关闭SparkContext:在应用程序结束时,需要关闭SparkContext释放资源。

4. Spark 中 RDD 的作用

  • 分布式数据处理:RDD 可以将数据分布在 Spark 集群的多个节点上,允许并行处理大规模数据集。

  • 容错性:RDD 具有弹性(Resilient)特性,能够自动恢复节点故障导致的数据丢失。通过 RDD 的分区信息和转换操作日志,Spark 可以重新计算丢失的数据,保证计算的完整性。

  • 并行计算:RDD 支持并行操作,允许在集群中的多个节点上同时执行计算任务,从而提高计算速度和吞吐量。

  • 不可变性:RDD 中的数据是不可变的,一旦创建就不能被修改。这种不可变性确保了数据的安全性和一致性,同时也方便了并行计算和容错机制的实现。

  • 高级操作:RDD 提供了丰富的转换操作和行动操作,可以进行各种数据处理、过滤、聚合等操作,以及对数据集进行持久化、分区控制等高级功能。

5.转换算子(Transformations)

  • map(func):对RDD中的每个元素应用一个函数,将其转换为另一个RDD。
  • filter(func):根据给定的条件过滤出RDD中的元素。
  • flatMap(func):类似于map,但每个输入项可以映射到多个输出项(例如,返回一个序列而不是单个项)。
  • mapPartitions(func):与map类似,但在每个分区上运行,可以提高效率。
  • mapPartitionsWithIndex(func):与mapPartitions类似,但函数还接收分区索引作为参数。
  • sample(withReplacement, fraction, seed):从RDD中随机采样一定比例的数据。
  • union(otherDataset):将两个RDD合并成一个RDD。
  • intersection(otherDataset):返回两个RDD的交集。
  • distinct([numTasks])):去除RDD中的重复项。
  • groupByKey([numTasks]):将具有相同键的元素分组在一起。
  • reduceByKey(func, [numTasks]):在每个键上执行聚合操作。
  • sortByKey([ascending], [numTasks]):按键对RDD进行排序。
  • join(otherDataset, [numTasks]):对两个RDD进行内连接。
  • cogroup(otherDataset, [numTasks]):对两个RDD进行联合分组。
  • cartesian(otherDataset):对两个RDD执行笛卡尔积。

注意:以下算子会导致shuffle(重分区)

  • groupByKey(): 这个算子会将数据按照键值对中的键进行分组,并将具有相同键的所有值聚合到一个列表中。由于这个操作需要将具有相同键的数据聚合在一起,因此可能需要数据重新分区。

  • reduceByKey(): 这个算子会将具有相同键的数据进行聚合,通过用户提供的函数进行归约操作。这个操作也会导致数据重新分区。

  • sortByKey(): 当你使用sortByKey()算子对键值对RDD进行排序时,Spark需要将具有相同键的数据聚合在一起,然后对每个键值对的键进行排序。这可能会导致数据重新分区。

  • join(): 当你使用join()算子将两个RDD进行连接时,Spark需要根据连接键重新组织数据,以便具有相同键的数据可以被合并在一起。这也可能导致数据重新分区。

  • distinct(): 当你调用distinct()算子时,Spark需要将所有的数据进行全局去重,这可能需要将数据重新分区。

  • repartition(): 明确调用repartition()算子会导致数据重新分区,你可以通过指定分区数量来重新分配数据。

  • coalesce(): 与repartition()类似,但coalesce()算子可以在减少分区数量时尝试尽量减少数据移动。

6.行动算子(Actions)

  • reduce(func):通过指定的函数将RDD中的元素聚合为单个结果。
  • collect():将RDD中的所有元素收集到驱动程序节点上,可能会导致内存溢出,谨慎使用。
  • count():返回RDD中的元素数量。
  • first():返回RDD中的第一个元素。
  • take(n):返回RDD中的前n个元素。
  • takeSample(withReplacement, num, [seed]):从RDD中随机采样指定数量的元素。
  • takeOrdered(n, [ordering]):返回RDD中的前n个元素,按指定顺序排序。
  • saveAsTextFile(path):将RDD中的元素保存为文本文件。
  • saveAsSequenceFile(path):将RDD中的元素保存为Hadoop SequenceFile。
  • countByKey():对键进行计数,返回一个map。
  • foreach(func):对RDD中的每个元素应用一个函数,通常用于副作用操作(如更新共享变量或输出到外部系统)。

7.Shuffle的原理

  • 数据划分(Partitioning):在执行Shuffle之前,Spark将数据划分为若干个分区,这些分区可以分布在集群的不同节点上。分区的划分通常基于哈希函数或者范围进行。
  • 数据写入(Map端):在Map阶段,每个Executor(或者Task)会处理一部分数据,它们将对数据进行处理并生成键值对。这些键值对将根据Shuffle操作的要求被写入到不同的分区中。
  • 数据传输(Shuffle):当所有Map任务完成了数据的处理和写入,Shuffle阶段就开始了。在Shuffle阶段,Spark将根据键的哈希值或者范围,将数据从Map任务所在的节点传输到Reduce任务所在的节点上。
  • 数据读取(Reduce端):当数据传输完成,Reduce任务将从不同节点接收到属于同一个分区的数据。然后,它们将对这些数据进行合并、聚合等操作,生成最终的结果。

8.请介绍一下Spark中的广播变量和累加器的作用及使用场景

        广播变量(Broadcast Variables)和累加器(Accumulators)是两种用于在分布式计算中共享数据和收集信息的重要机制。

  • 广播变量(Broadcast Variables):

作用:广播变量允许开发者在每个节点上缓存一个只读的变量,而不是为每个任务都复制一份。这样可以有效地减少通信开销,并且可以提高性能。

使用场景: - 在任务执行过程中需要使用大型只读数据集时,如机器学习模型参数、字典、配置文件等。 - 在任务中需要频繁地访问相同的数据集合,但数据集合不会在任务执行过程中发生变化。

示例:假设在分布式任务中需要将一个大型的字典加载到每个节点上,并在每个任务中使用该字典进行查找操作,这时就可以使用广播变量来共享该字典,避免在每个任务中都复制一份字典。

  • 累加器(Accumulators):

作用:累加器是一种仅能进行累加操作(添加、合并)的变量,用于在任务执行过程中收集信息或计算统计数据。

使用场景: - 在任务执行过程中需要对全局的某种指标进行统计,如计数、求和、最大值、最小值等。 - 在调试或监控任务时,需要收集任务执行过程中的一些信息或指标。

示例:假设需要统计一个大型数据集中某个特定属性的出现次数,可以使用累加器在任务执行过程中进行计数操作,最后得到全局的出现次数。

在使用广播变量和累加器时需要注意以下几点:

  • 广播变量和累加器都是在驱动器程序中创建,并在执行器中进行共享和使用。
  • 广播变量的值在驱动器程序中修改后不会影响到已经广播到各个节点的值。
  • 累加器通常用于进行累加操作,但在任务执行过程中无法获取累加器的值,只能在任务执行完成后通过驱动器程序获取其值。

9.Spark 与 Hadoop 的区别

  • 计算模型:Hadoop 使用 MapReduce 计算模型,将数据存储在磁盘上,并在节点之间进行数据传输。而Spark 提供了基于内存的计算,利用内存中的数据进行迭代计算,从而大大加速了处理速度。
  • 抽象层次:Spark 提供了比 MapReduce 更高级的抽象,如RDD、DataFrame和Dataset,使得开发人员能够更方便地编写复杂的数据处理逻辑。
  • 处理类型:Hadoop 更适合批处理任务,而Spark 不仅支持批处理,还支持交互式查询、流式处理和机器学习等多种处理模式。

10.Spark SQL的优化技巧

  • 合理使用分区和分桶:将数据根据业务需求进行合理的分区和分桶,可以减少不必要的数据扫描,提高查询效率。
  • 避免全表扫描:尽量避免全表扫描,使用谓词下推(Predicate Pushdown)和分区剪枝(Partition Pruning)等技术来减少不必要的数据扫描。
  • 合理设置shuffle分区数: 对于需要Shuffle操作的操作,如join、group by等,合理设置shuffle分区数可以避免数据倾斜,提高性能。
  • 使用合适的存储格式:选择合适的数据存储格式,如Parquet、ORC等,可以减少存储空间占用和IO开销,提高查询性能。
  • 列式存储:利用列式存储的优势,可以减少不必要的IO和内存消耗,提高查询性能。
  • 数据压缩: 在存储数据时进行压缩,可以减少存储空间占用和IO开销,同时提高数据读取速度。
  • 适当增加executor内存和核心数:根据作业的需求和集群的资源情况,适当增加executor的内存和核心数,可以提高作业的并行度和性能。
  • 使用适当的缓存策略: 对频繁访问的数据集合进行缓存,可以减少重复计算和IO开销,提高查询性能。
  • 避免使用UDF: 尽量避免使用自定义函数(UDF),因为它们可能导致整行数据的反序列化和序列化,影响查询性能。
  • 优化SQL语句:优化SQL语句的写法,如避免使用SELECT *、使用JOIN ON替代WHERE中的条件等,可以提高查询性能。

11.在Spark中如何处理数据倾斜问题

        数据倾斜指的是在数据处理过程中,某些特定的数据分区或键值对数据量过大或过小,导致任务的执行速度不均匀,从而影响整个作业的性能。数据倾斜可能是由于数据分布不均匀、键值分布不均匀、Hash函数不均匀等原因引起的。

  • 重新分区:通过增加或减少分区的数量,尝试平衡数据的分布。可以使用`repartition()`或者`coalesce()`等方法重新分区。
  • 使用自定义分区器:对于特定的场景,可以使用自定义的分区器来更好地控制数据的分布。比如,可以根据数据的特征自定义Hash函数,使得数据分布更均匀。
  • 数据预处理:在数据处理之前,可以对数据进行预处理,对数据进行聚合、采样等操作,以减少数据倾斜的发生。
  • 使用随机前缀:对于键值对数据倾斜的情况,可以给键添加随机前缀,使得数据更均匀地分布在不同的分区中。
  • 使用多阶段聚合:对于需要进行聚合操作的场景,可以采用多阶段的聚合策略,先进行局部聚合,再进行全局聚合,以减少单个分区的数据量。
  • 增加资源:如果可能的话,增加集群的资源(CPU、内存、带宽等),可以缓解数据倾斜带来的性能问题。
  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值