Spark面试题(七)——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算子

微信图片_20220426153042.png


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()等都能够受益

微信图片_20220426153046.png


总结:如果遇到一个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频率,提升性能。

目录
相关文章
|
9月前
|
存储 关系型数据库 MySQL
2024年Java秋招面试必看的 | MySQL调优面试题
随着系统用户量的不断增加,MySQL 索引的重要性不言而喻,对于后端工程师,只有在了解索引及其优化的规则,并应用于实际工作中后,才能不断的提升系统性能,开发出高性能、高并发和高可用的系统。 今天小编首先会跟大家分享一下MySQL 索引中的各种概念,然后介绍优化索引的若干条规则,最后利用这些规则,针对面试中常考的知识点,做详细的实例分析。
368 0
2024年Java秋招面试必看的 | MySQL调优面试题
|
Java 中间件 关系型数据库
不会性能调优,被面试官狂虐!全靠阿里Java性能调优全彩手册死撑
性能调优从来不是一件容易的事。 可是在工作和面试中,JVM调优、MySQL调优、各种分布式中间件的调优又都是绕不过的。
|
3月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
5月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
531 37
|
4月前
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。
|
6月前
|
关系型数据库 MySQL Java
面试官:说说MySQL调优?
面试官:说说MySQL调优?
118 5
面试官:说说MySQL调优?
|
9月前
|
分布式计算 Java 数据库连接
回答粉丝疑问:Spark为什么调优需要降低过多小任务,降低单条记录的资源开销?
回答粉丝疑问:Spark为什么调优需要降低过多小任务,降低单条记录的资源开销?
76 1
|
7月前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
119 3
|
7月前
|
缓存 监控 算法
Java面试题:讨论JVM性能调优的常见方法和技巧。
Java面试题:讨论JVM性能调优的常见方法和技巧。
71 1
|
7月前
|
存储 算法 安全
Java面试题:给定一个可能产生内存泄漏的场景,如何诊断并解决?实现一个生产者-消费者模型,使用适当的同步机制与并发工具类,Java并发工具包与框架:性能与调优
Java面试题:给定一个可能产生内存泄漏的场景,如何诊断并解决?实现一个生产者-消费者模型,使用适当的同步机制与并发工具类,Java并发工具包与框架:性能与调优
53 0