Spark系列面试题

1、程序开发调优 :避免创建重复的RDD

需要对名为“hello.txt”的HDFS文件进行一次map操作,再进行一次reduce操作。也就是说,需要对一份数据执行两次算子操作。

错误的做法

对于同一份数据执行多次算子操作时,创建多个RDD。//这里执行了两次textFile方法,针对同一个HDFS文件,创建了两个RDD出来,然后分别对每个RDD都执行了一个算子操作。

这种情况下,Spark需要从HDFS上两次加载hello.txt文件的内容,并创建两个单独的RDD;//第二次加载HDFS文件以及创建RDD的性能开销,很明显是白白浪费掉的。

val rdd1 = sc.textFile("hdfs://master:9000/hello.txt")
rdd1.map(...)
val rdd2 = sc.textFile("hdfs://master:9000/hello.txt")
rdd2.reduce(...)

正确的用法

对于一份数据执行多次算子操作时,只使用一个RDD。

2、程序开发调优 :尽可能复用同一个RDD

错误的做法

有一个<long , String>格式的RDD,即rdd1。

接着由于业务需要,对rdd1执行了一个map操作,创建了一个rdd2,而rdd2中的数据仅仅是rdd1中的value值而已,也就是说,rdd2是rdd1的子集。

JavaPairRDD<long , String> rdd1 = ...
JavaRDD<string> rdd2 = rdd1.map(...)

分别对rdd1和rdd2执行了不同的算子操作。

rdd1.reduceByKey(...)
rdd2.map(...)

正确的做法

rdd2的数据完全就是rdd1的子集而已,却创建了两个rdd,并对两个rdd都执行了一次算子操作。

此时会因为对rdd1执行map算子来创建rdd2,而多执行一次算子操作,进而增加性能开销。

其实在这种情况下完全可以复用同一个RDD。

我们可以使用rdd1,既做reduceByKey操作,也做map操作。

JavaPairRDD<long , String>
rdd1 = ...rdd1.reduceByKey(...)
rdd1.map(tuple._2...)

3、程序开发调优 :对多次使用的RDD进行持久化

正确的做法

cache()方法表示:使用非序列化的方式将RDD中的数据全部尝试持久化到内存中。

此时再对rdd1执行两次算子操作时,只有在第一次执行map算子时,才会将这个rdd1从源头处计算一次。

第二次执行reduce算子时,就会直接从内存中提取数据进行计算,不会重复计算一个rdd。

val rdd1 = sc.textFile("hdfs://192.168.0.1:9000/hello.txt").cache()
rdd1.map(...)
rdd1.reduce(...)

序列化的方式可以减少持久化的数据对内存/磁盘的占用量,进而避免内存被持久化数据占用过多,从而发生频繁GC。

val rdd1 = sc.textFile("hdfs://192.168.0.1:9000/hello.txt")  .persist(StorageLevel.MEMORY_AND_DISK_SER)
rdd1.map(...)
rdd1.reduce(...)

注意:通常不建议使用DISK_ONLY和后缀为_2的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,导致网络较大开销

4、程序开发调优 :尽量避免使用shuffle类算子

如果有可能的话,要尽量避免使用shuffle类算子,最消耗性能的地方就是shuffle过程。

shuffle过程中,各个节点上的相同key都会先写入本地磁盘文件中,然后其他节点需要通过网络传输拉取各个节点上的磁盘文件中的相同key。而且相同key都拉取到同一个节点进行聚合操作时,还有可能会因为一个节点上处理的key过多,导致内存不够存放,进而溢写到磁盘文件中。因此在shuffle过程中,可能会发生大量的磁盘文件读写的IO操作,以及数据的网络传输操作。磁盘IO和网络数据传输也是shuffle性能较差的主要原因。

尽可能避免使用reduceByKey、join、distinct、repartition等会进行shuffle的算子,尽量使用map类的非shuffle算子

传统的join操作会导致shuffle操作。

因为两个RDD中,相同的key都需要通过网络拉取到一个节点上,由一个task进行join操作。

val rdd3 = rdd1.join(rdd2)

Broadcast+map的join操作,不会导致shuffle操作。

使用Broadcast将一个数据量较小的RDD作为广播变量。

val rdd2Data = rdd2.collect()
val rdd2DataBroadcast = sc.broadcast(rdd2Data)
val rdd3 = rdd1.map(rdd2DataBroadcast...)

注意:以上操作,建议仅仅在rdd2的数据量比较少(比如几百M,或者一两G)的情况下使用。因为每个Executor的内存中,都会驻留一份rdd2的全量数据。

5、程序开发调优 :使用map-side预聚合的shuffle操作

如果因为业务需要,一定要使用shuffle操作,无法用map类的算子来替代,那么尽量使用可以map-side预聚合的算子,类似于MapReduce中的本地combiner。map-side预聚合之后,每个节点本地就只会有一条相同的key,因为多条相同的key都被聚合起来了。其他节点在拉取所有节点上的相同key时,就会大大减少需要拉取的数据数量,从而也就减少了磁盘IO以及网络传输开销。

建议使用reduceByKey或者aggregateByKey算子来替代掉groupByKey算子

6、程序开发调优 :使用高性能的算子

使用reduceByKey/aggregateByKey替代groupByKey : map-side

使用mapPartitions替代普通map : 函数执行频率

使用foreachPartitions替代foreach : 函数执行频率

使用filter之后进行coalesce操作 : filter后对分区进行压缩

使用repartitionAndSortWithinPartitions替代repartition与sort类操作

repartitionAndSortWithinPartitions是Spark官网推荐的一个算子,官方建议,如果需要在repartition重分区之后,还要进行排序,建议直接使用repartitionAndSortWithinPartitions算子

7、程序开发调优 :广播大变量

有时在开发过程中,会遇到需要在算子函数中使用外部变量的场景(尤其是大变量,比如100M以上的大集合),那么此时就应该使用Spark的广播(Broadcast)功能来提升性能。

默认情况下,Spark会将该变量复制多个副本,通过网络传输到task中,此时每个task都有一个变量副本。如果变量本身比较大的话(比如100M,甚至1G),那么大量的变量副本在网络中传输的性能开销,以及在各个节点的Executor中占用过多内存导致的频繁GC,都会极大地影响性能。

广播后的变量,会保证每个Executor的内存中,只驻留一份变量副本,而Executor中的task执行时共享该Executor中的那份变量副本。

8、程序开发调优 :使用Kryo优化序列化性能

1)在算子函数中使用到外部变量时,该变量会被序列化后进行网络传输。

2)将自定义的类型作为RDD的泛型类型时(比如JavaRDD,Student是自定义类型),所有自定义类型对象,都会进行序列化。因此这种情况下,也要求自定义的类必须实现Serializable接口。

3)使用可序列化的持久化策略时(比如MEMORY_ONLY_SER),Spark会将RDD中的每个partition都序列化成一个大的字节数组。

Spark默认使用的是Java的序列化机制,你可以使用Kryo作为序列化类库,效率要比Java的序列化机制要高

// 创建SparkConf对象。
val conf = new SparkConf().setMaster(...).setAppName(...)
// 设置序列化器为KryoSerializer。
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// 注册要序列化的自定义类型。
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))

9、程序开发调优 :分区Shuffle优化

当遇到userData和events进行join时,userData比较大,而且join操作比较频繁,这个时候,可以先将userData调用了 partitionBy()分区,可以极大提高效率。

cogroup()、 groupWith()、join()、leftOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、 combineByKey() 以及 lookup()等都能够受益

总结:如果遇到一个RDD频繁和其他RDD进行Shuffle类操作,比如 cogroup()、 groupWith()、join()、leftOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、 combineByKey() 以及 lookup()等,那么最好将该RDD通过partitionBy()操作进行预分区,这些操作在Shuffle过程中会减少Shuffle的数据量

10、程序开发调优 :优化数据结构

Java中,有三种类型比较耗费内存:

1)对象,每个Java对象都有对象头、引用等额外的信息,因此比较占用内存空间。

2)字符串,每个字符串内部都有一个字符数组以及长度等额外信息。

3)集合类型,比如HashMap、LinkedList等,因为集合类型内部通常会使用一些内部类来封装集合元素,比如Map.Entry

Spark官方建议,在Spark编码实现中,特别是对于算子函数中的代码,尽量不要使用上述三种数据结构,尽量使用字符串替代对象,使用原始类型(比如Int、Long)替代字符串,使用数组替代集合类型,这样尽可能地减少内存占用,从而降低GC频率,提升性能。

Spark面试题(七)——Spark程序开发调优的更多相关文章

  1. Spark学习之路 (八)SparkCore的调优之开发调优

    摘抄自:https://tech.meituan.com/spark-tuning-basic.html 前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一.Spark ...

  2. Spark(六)Spark之开发调优以及资源调优

    Spark调优主要分为开发调优.资源调优.数据倾斜调优.shuffle调优几个部分.开发调优和资源调优是所有Spark作业都需要注意和遵循的一些基本原则,是高性能Spark作业的基础:数据倾斜调优,主 ...

  3. Spark性能优化:开发调优篇

    1.前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一.Spark的功能涵盖了大数据领域的离线批处理.SQL类处理.流式/实时计算.机器学习.图计算等各种不同类型的计算 ...

  4. Spark学习之路 (八)SparkCore的调优之开发调优[转]

    前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一.Spark的功能涵盖了大数据领域的离线批处理.SQL类处理.流式/实时计算.机器学习.图计算等各种不同类型的计算操作 ...

  5. Spark性能优化--开发调优与资源调优

    参考: https://tech.meituan.com/spark-tuning-basic.html https://zhuanlan.zhihu.com/p/22024169 一.开发调优 1. ...

  6. 针对UDP丢包问题,进行系统层面和程序层面调优

    转自:https://blog.csdn.net/xingzheouc/article/details/49946191 1. UDP概念 用户数据报协议(英语:User Datagram Proto ...

  7. 【Spark篇】---Spark中内存管理和Shuffle参数调优

    一.前述 Spark内存管理 Spark执行应用程序时,Spark集群会启动Driver和Executor两种JVM进程,Driver负责创建SparkContext上下文,提交任务,task的分发等 ...

  8. Spark Streaming概念学习系列之SparkStreaming性能调优

    SparkStreaming性能调优 合理的并行度 减少批处理所消耗时间的常见方式还有提高并行度.有以下三种方式可以提高并行度: 1.增加接收器数目 有时如果记录太多导致单台机器来不及读入并分发的话, ...

  9. Spark 官网提到的几点调优

    1. 数据序列化 默认使用的是Java自带的序列化机制.优点是可以处理所有实现了java.io.Serializable 的类.但是Java 序列化比较慢. 可以使用Kryo序列化机制,通常比Java ...

随机推荐

  1. 【vue】两个页面间传参 - props

    目录 Step1 设置可以 props 传递数据 Step2 跳转前页面中传递数据 Step3 跳转后的页面接收数据 从 A 页面跳转到 B 页面, 参数/数据通过 props 传递到 B 页面,这种 ...

  2. 踩坑系列《九》 无法获取实体类xxx对应的表名

    话不多说,直接说明原因 类似于 @MapperScan(basePackages = "com.hyxiao.user.mapper") 启动类的mapper扫描注解的导入包正确的 ...

  3. 10.1 HTTP

    1.跨网络的主机间通讯 套接字Socket是进程间通信IPC的一种实现,允许位于不同主机(或同一主机)上不同进程之间通信和数据交换 在建立通信连接的每一端,进程间的传输要有两个标志:IP地址和端口号, ...

  4. JUC多线程之ThreadPoolExecutor类任务执行流程

    ThreadPoolExecutor类: ThreadPoolExecutor是我们最常用的一个线程池类,它实现了AbstractExecutorService接口.首先来看一下它的构造器及相关关键变 ...

  5. 零基础怎么学Java?Java的运行机制是什么?Java入门基础!

    Java语言是当前流行的一种程序设计语言,因其安全性.平台无关性.性能优异等特点,受到广大编程爱好者的喜爱. 想学习Java语言的同学对于Java的运行机制是必须要了解的!! 计算机高级语言的类型主要 ...

  6. FFT&原根&NTT&MTT

    FFT bilibili 3b1b视频讲解 核心过程: 原根 Definition 若 \(a\) 模 \(m\) 的阶等于 \(\varphi(m)\),则称 \(a\) 为模 \(m\) 的一个原 ...

  7. 在IDEA中创建SpringBoot项目01

    1.选择创建项目 2.填写项目信息 3. 4. 5.Finish后会下载,之后生成目录结构: 6.在自己的包目录结构下添加了Controllr和Entiy测试项目: Controller: 1 pac ...

  8. Vulnstack内网靶场2

    环境配置 内网2靶场由三台机器构成:WIN7.2008 server.2012 server 其中2008做为对外的web机,win7作为个人主机可上网,2012作为域控 网络适配器已经设置好了不用自 ...

  9. MIPS指令 MIPS架构

    华中科技大学 - 计算机组成原理 华中科技大学 - 计算机硬件系统设计 Microprocessor without Interlocked Pipleline Stages 无内部互锁流水级的微处理 ...

  10. WIFI Deauth攻击-爬坑笔记

    这里用Aircrack这款工具进行介绍: 准备工作:无线网卡连接电脑或者虚拟机(免驱的最好),如需驱动请自行安装驱动 1.将无线网卡接入测试电脑Linux虚拟机(装有Aircrack-ng) 2.测试 ...