spark调优

spark调优目录

spark调优之性能调优

1.合理的分配资源
2.设置并行度
3.重构RDD架构以及RDD持久化
4.广播变量
5.调节数据本地化等待时长

spark调优之JVM调优

1.jvm垃圾回收机制
2.降低cache操作的内存占比
3.调节executer对外内存与连接等待时长

spark调优之Shuffle调优

1.开启shuffle map端输出文件合并的机制
2.调节map端内存缓冲与reduce端内存的占比

spark调优之算子调优

1.map==>MapPartitions
2.filter==>filter().coalesce / filter==>case when then else end
3.foreach==>foreachPartirion
4.SparkSql无法设置并行度
5.group by key ==>reduce by key本地聚合

spark调优之问题解决

1.shuffle reduced端缓冲大小避免ooM
a.性能调优
b.reduce端缓冲(buffer)可能会出现的问题及解决方式
2.解决JVM GC导致的shuffle文件拉取失败

3.YARN队列资源不足导致的application直接失败

4.解决各种序列化导致的报错

5.解决算子函数返回NULL导致的问题

6.解决yarn-cluster模式的JVM栈内存溢出的问题,

spark调优实例

spark调优之性能调优

1.合理的分配资源

在执行spark任务的时候使用的是spark-submit shell脚本执行的,
	执行的时候分配executer的数量,CPUcores的数量,和driver端的内存
	a.增加executer数量的好处是:提高并行度,例如原来有三个executer,每个executer有两个CPUcores
	那么task就是六个,就是说同时运行的task只有六个,六个执行完毕之后再能执行下一个六个task,提高效率
	b.增加cpu_cores的数量:效果和上面的一样,增加了task并行的数量,提高效率
	c.增大driver端的内存:大概有三个好处
		(1)RDD进行计算缓存的时候,就可以有更多的数量缓存的内存中,更少的数据缓存到磁盘中,减少了磁盘IO
		(2)对于shuffle操作,reduce端,会需要内存来存放拉取数据进行聚合,如果内存不够也会写入磁盘,
		如果给executer分配更多的内存以后,就会有更少的数据需要写入磁盘,甚至不需要写入磁盘,减少IOj,提高效率.
		(3)对于task执行而言,会创建很多的对象,如果内存不够会导入JVM堆内存满了,导致多次的垃圾回收,
		程序会等带JVM垃圾回收完毕之后继续运行,速度变慢,效率下降

2.设置并行度

合理的并行度的设置,应该是设置的足够大,大到可以完全利用你的集群资源task数量,设置成spark application总CPUcore数量的2~3,比如150个CPUcore基本要设置task数量为300~500.
代码实现:
SparkConf conf = new SparkConf()
  								.set("spark.default.parallelism", "500")

3.重构RDD架构以及RDD持久化

将多次复用的RDD持久化,就是将RDD的数据缓存到内存或者磁盘中,再次使用的时候直接去内存或者磁盘中拉取数据即可,如果数据量较大,占用较多的内存,
	可以将RDD的数据系列化后缓存,将数据序列化为字节数组减小内存,不过使用序列化在获取数据时还需反序列化.
	Kryo序列化机制

4.广播变量

例如说在spark执行任务的时候需要一份固定的数据,每一个executer有若干个task那么task执行任务的时候都要去driver拉取这个固定的数据,这样效率低下,数据复用使用广播变量,
	将固定的数据广播到executer中,那么每个executer上的task都会到自己的executer上读取数据,提高了效率.

5.调节数据本地化等待时长

spark在执行的过称中,理想的情况是task处理的数据正好在所在的节点上,这样就不用网络传输,那么如果所在数据节点上的计算任务已经满了,那么这个task就会分配到其他节点上运行,
那么数据要从有数据的那个节点上拉取,进行网络传输,影响效率.new SparkConf().set("spark.locality.wait","10")

spark调优之JVM调优

jvm的组成

栈内存:本地方法栈,java虚拟机栈,程序计数器
堆内存:新生代,生存代,年轻代,老年代,长久代,不同的区域GC(垃圾回收)机制不同,
	新的对象实例创建会放在Eden(伊甸)区域,随着对象增多,Eden消耗内存接近Eden的最大值,huich
	所有的对象实例,GC的主要对象
方法区:存放各种静态方法,和静态变量

1.jvm的GC(Garbage垃圾 Collection回收)

堆内存存放我们创建的一些对象,有老年代和年轻代。理想情况下,老年代都是放一些生命周期很长的对象,数量应该是很少的,比如数据库连接池。我们在 spark task 执行算子函数(我们自己写的), 可能会创建很多对象,这些对象都是要放入 JVM 年轻代中的。 
每一次放对象的时候,都是放入eden(伊甸园)区域,和其中一个survivor(存活)区域。另外一个survivor 区域是空闲的.eden(伊甸园)区域和一个survivor区域放满了以后(spark运行过程中,产生的对象实在太多了),就会触 发 minor(次要) gc,小型垃圾回收。把不再使用的对象,从内存中清空,给后面新创建的对象腾出来点儿地 方。清理掉了不再使用的对象之后,那么也会将存活下来的对象(还要继续使用的),放入之前空闲的那 一个 survivor 区域中。这里可能会出现一个问题。默认 eden、survior1 和 survivor2 的内存占比是 8:1:1。 问题是,如果存活下来的对象是 1.5,一个 survivor 区域放不下。此时就可能通过 JVM 的担保机制(不 同 JVM 版本可能对应的行为),将多余的对象,直接放入老年代了。 如果你的 JVM 内存不够大的话,可能导致频繁的年轻代内存满溢,频繁的进行 minor gc。频繁的 minor gc 会导致短时间内,有些存活的对象,多次垃圾回收都没有回收掉。会导致这种短生命周期(其实不 一定是要长期使用的)对象,年龄过大,垃圾回收次数太多还没有回收到,跑到老年代。 老年代中,可能会因为内存不足,囤积一大堆,短生命周期的,本来应该在年轻代中的,可能马上就要被回收掉的对象。此时,可能导致老年代频繁满溢。频繁进行full gc(全局/全面垃圾回收)。full gc就会去回收老年代中的对象。full gc由于这个算法的设计,是针对的是,老年代中的对象数量很少,满溢进行full gc的频率应该很少,因此采取了不太复杂,但是耗费性能和时间的垃圾回收算法。full gc很慢。full gc/minor gc,无论是快,还是慢,都会导致jvm的工作线程停止工作,stop the world。简而言之,就是说,gc的时候,spark停止工作了。等着垃圾回收结束。
	内存不充足的时候,出现的问题:
	(1)频繁minor gc,也会导致频繁spark停止工作
	(2)老年代囤积大量活跃对象(短生命周期的对象),导致频繁full gc,full gc时间很长,短则数十秒,长则数分钟,甚至数小时。可能导致spark长时间停止工作。
	(3)严重影响咱们的spark的性能和运行的速度

2.降低cache操作的内存占比

spark 中,堆内存又被划分成了两块,一块是专门用来给 RDD 的 cache、persist 操作进行 RDD 数据缓 存用的。另外一块用来给 spark 算子函数的运行使用的,存放函数中自己创建的对象。 
	默认情况下,给 RDD cache 操作的内存占比,是 0.660%的内存都给了 cache 操作了。但是问题是, 如果某些情况下 cache 不是那么的紧张,问题在于 task 算子函数中创建的对象过多,然后内存又不太 大,导致了频繁的 minor gc,甚至频繁 full gc,导致 spark 频繁的停止工作。性能影响会很大。 针对上述这种情况,可以在任务运行界面,去查看你的 spark 作业的运行统计,可以看到每个 stage 的运行情况,包括每个 task 的运行时间、gc 时间等等。如果发现 gc 太频繁,时间太长。此时就可以 适当调节这个比例。 
	降低 cache 操作的内存占比,大不了用 persist 操作,选择将一部分缓存的 RDD 数据写入磁盘,或者 序列化方式,配合 Kryo 序列化类,减少 RDD 缓存的内存占用。降低 cache 操作内存占比对应的,算子函数的内存占比就提升了。这个时候,可能就可以减少 minor gc 的频率,同时减少 full gc 的频率。 对性能的提升是有一定的帮助的。 
	一句话,让 task 执行算子函数时,有更多的内存可以使用。 
	spark.storage.memoryFraction,0.6 -> 0.5 -> 0.4 -> 0.2 

3.调节executer对外内存与连接等待时长

在执行spark-submit命令的时候  使用--conf的方法调节堆外内存
	--conf spark.yarn.executor.memoryOverhead=2048
	
	调节连接等待时长
	executor会从本地拉取数据  如果本地没有 会从替他节点拉取数据 此时如果jvm正在垃圾回收 那么spqrk会停止数据  就不会拉取数据  这个默认的连接是60秒了还没有拉取到数据 呢么就会宣告失败, 那么调节连接等待时长会很有必要 避免部分会因为超时而失败的任务
	-conf spark.core.connection.ack.wait.timeout=300

spark调优之Shuffle调优

1.开启shuffle map端输出文件合并的机制

如果spqrk任务会产生大量的map文件 shuffle过程中会产生大量的磁盘文件 会影响效率
	基本上spark 作业的性能,都消耗在 shuffle 中了
	默认情况下,是不开启的,就是会发生如上所述的大量 map 端输出文件的操作,严重影响性能
	new SparkConf().set("spark.shuffle.consolidateFiles", "true")

2.调节map端内存缓冲与reduce端内存的占比

map操作时需要将数据先缓存到内存中,当达到一定大小时(默认32K)会溢写磁盘,为了减少溢写的次数 可适当调大一点
	调节 map task 内存缓冲:spark.shuffle.file.buffer,默认 32k 
	
	默认情况下executor分给reduce task的内存比例仅有0.2  那么内存太小 每次task拉取来的数据有可能会放不下 需要溢写磁盘 这样会影响效率
	调节 reduce 端聚合内存占比:spark.shuffle.memoryFraction,0.2
	适量的调大一点 也不能过大 因为内存资源有限 这里调的太大 其他地方的占比就小了 
	
	总之调节这两个参数都是为了减少磁盘的交互 而且减少了后面聚合读取磁盘文件的数量
	提高运行效率

spark调优之算子调优

在spark中,最基本的与原则就是每一个task处理一个RDD的partition

1.Map==>MapPartitions

优势:在spark程序中例如map(func)连接mysql,参数func逻辑后返回的数据组成一个新的RDD,所以每一行数据都会经过这个函数处理,假设数据有M行,map(func)就会通过M次网络IO向Executor发送任务进行计算的,
每一次连接都会创建一个连接一个连接就是一个对象,在这个程序的堆内存中就会创建M个实例对象,mappartition()的方法实际上执行次数等于分区数N,虽然通过网络IO向Executor发送N次任务但是程序最终还是执行了M行,N(分区数)一定是小于M(数据行数)的所以通过网络次数减少,效率是一定会提高的.
弊端:mappartition()是把计算后的数据通过网络IO发送给Executor的如果计算之后的数据大于Executor的内存就会报OOM异常个GC异常,
所以内存足够的话可以考虑用mappartition()的算子来提高效率.
补充:
OOM异常是:OutOfMemoryError(内存溢出异常)
GC异常是:频繁的垃圾回收,导致数据等待,如果连续等待的次数超过了5次还没有等待垃圾回收完毕,就会GC导致程序超时而中断,我们的内存分为,执行内存和缓存内存(cached memory),默认缓存内存(cached memory)60%,我们将缓存内存调小,就会增大执行内存,就会加快垃圾回收,从而减少GC的出现概率.

2.2.filter==>filter().coalesce 或者 filter==>case when then else end

filter操作后每个partition的数据会有所变化 但task的数量不变  那么后续的task就会数据倾斜,或者数据量可以压缩为更少的分区 减少task的数量  那么就造成了task资源的浪费
	解决办法1:调用 coalesce 算子 压缩partition的数据,节省task,将节省出来的资源,去执行其他的程序,提高整体效率.
	解决办法2:调用case when then else end函数,将需要反复filter的程序,一遍过.

3.foreach==>foreachPartirion

默认的forech 每一条数据都会执行一次function ,如果需要连接数据库  那么有多少条数据  就会创建多少次的链接和sql语句的执行,在堆内存中就会产生多少个对象的实例,消耗资源,影响效率.
foreachpartition算子 在每个partition中只会创建一次链接和只发送一次的sql语句和参数 提升性能  但是这个算子和maopartiton算子一样如果数据量很大的话,还是会有OOM GC 的风险的!!

4.SparkSql无法设置并行度

在sqarksql中 我们设置的并行度是不起作用的 因为sqarksql在读取hive表的时候  会根据hive表所对应hdfs数据的块数量来决定的  例如我们设置的是100并行度  而文件的切块是10个  那么就会导致第一层的stage会很慢 第二层的stage会很块(对比之下)。
	解决问题:使用 repartition 算子去重新进行分区,此时可以分成多个partition  那么repartition分区后的RDD的并行度就会按照我们设置的运行了。

5.group by key ==>reduce by key本地聚合

相对与groupbykey来说  使用本地聚合的好处是 在本地聚合以后 map端的数据量就变少了  减少了磁盘IO
对于下一个stage来说 拉取的数据也少了 在reduce端占用的内存就变少了

spark调优之问题解决

1.shuffle reduced端缓冲大小避免ooM

a.性能调优
	map是不断的拉取数据的  reduce也是在不断的拉取数据 如果数据量不是很大的话 map端输出一点 reduce就会拉取一点  在资源充足的情况下 可以适当的增大reduce 缓存的大小 减少拉取的次数 可以提升性能  当然必须是在性能资源充足的情况下
b.reduce端缓冲(buffer)可能会出现的问题及解决方式
	如果数据量很大 而且map端输出的速度很快时  reduce端的task就会每次将数据拉满后计算 这样的话再加上计算任务会产生很多的对象  那么有可能会造成内存不足OOM的情况  
需要将reduce端的缓存调小  避免内存溢出
	ew SparkConf().set(spark.reducer.maxSizeInFlight,”48)

2.解决JVM GC导致的shuffle文件拉取失败

比如再stage在拉取文件的时候  上一个stage正在进行垃圾回收  默认情况是会重复拉取3次  每次延迟5秒  如果15后还没有拉取到数据 那么就会失败
	调节重复次数和延迟时间
	spark.shuffle.io.maxRetries 60 
	spark.shuffle.io.retryWait 60s

3.YARN队列资源不足导致的application直接失败

用yarn方式提交作业时  实际占用的资源会比设置的资源要大一些  如果我们设置的资源占比时整个集群的一半  那么实际的占用要大于1/2  如果此时又提交了同样的任务  那么资源调度不过来会报错
	解决办法:
	(1)限制任务的个数 在sqarksubmit shell设置中限制 每次只提交一个任务  确保这个任务的执行
	(2)采用调度分区的方式 将长时间作业的任务  和短时间的任务区分开来
	(3)使用性能调优  计算使用的资源 尽量每一次的任务都达到最满的资源利用
	(4)通过线程池的方式  一个线程池对应一个资源队列

4.解决各种序列化导致的报错

1、你的算子函数里面,如果使用到了外部的自定义类型的变量,那么此时,就要求你的自定义类型, 必须是可序列化的。
	、如果要将自定义的类型,作为 RDD 的元素类型,那么自定义的类型也必须是可以序列化的
	、不能在上述两种情况下,去使用一些第三方的,不支持序列化的类型

5.解决算子函数返回NULL导致的问题

1、在返回的时候,返回一些特殊的值,不要返回 null,比如“-9992、在通过算子获取到了一个 RDD 之后,可以对这个 RDD 执行 filter 操作,进行数据过滤。filter 内, 可以对数据进行判定,如果是-999,那么就返回 false,给过滤掉就可以了。

6.解决yarn-cluster模式的JVM栈内存溢出的问题,

有时候yarn-client下任务会执行  但是在yarn-cluster上运行不了会报出 JVM 的 PermGen(永久代)的内存溢出 这个原因是yarn-cluster模式默认的永久代内存小与yarn-client的默认大小 
	spark-submit 脚本中,加入以下配置即可: 
	--conf spark.driver.extraJavaOptions="-XX:PermSize=128M -XX:MaxPermSize=256M" 
	如果sql语句中又大量的or语句  可能就会出现一个 driver 端的 jvm stack overflow,JVM 栈 
	内存溢出的问题。那么需要优化sql语句  可以将这个复杂的语句拆分为几个  大概每个语句or控制在100以内
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值