spark troubleshooting处理
1 调节reduce端缓冲区大小避免OOM异常
1.1 问题描述:
对于map端不断产生的数据,reduce端会不断拉取一部分数据放入到缓冲区,进行聚合处理;
当map端数据特别大时,reduce端的task拉取数据是可能将全部的缓冲区都填满的,此时进行reduce聚合处理时创建大量的对象,导致OOM异常。
1.2 解决方案:
当由于以上原因导致OOM的异常出现时,可以通过减小reduce端缓冲区大小来避免OOM异常的出现。(由于内存不足,所以减少每次reduce端的拉取数据量,比如:可以调整为12M)
但是在内存充足的情况下,也可以适当增大reduce端缓冲区的大小,从而减少reduce端拉取数据的次数,提高性能。(由于我们内存充足,所以将参数设置成96M,以减少reduce端数据拉取次数,提高性能)
1.3 参数设置:
名称:spark.reducer.maxSizeInFlight 默认值 48M
参数含义:决定每个reducer从远端executor请求的数据量。
2 解决JVM GC导致的shuffle文件拉取失败
2.1 问题描述:
下一个stage的task去拉取上一个stage的task的输出文件时,如果正好上一个stage正处在full gc的情况下(所有线程都停止运行),它们之间是通过netty通信的,就会出现很长时间拉取不到时间,此时就会报shuffle file not found的错误;但是下一个stage又重新提交task就不会出现问题了。
2.2 解决方案:
调节最大尝试拉取次数:
参数名称:spark.shuffle.io.maxRetries
参数含义:shuffle文件拉取的时候,如果没有拉取到(拉取失败),最多重试次数,默认为3次。
调节每次拉取最大的等待时长:
参数名称:spark.shuffle.io.retryWait
参数含义:每一次拉取文件的时间间隔,默认为5秒。默认情况下,重试所导致的最大延迟为15秒,计算公式spark.shuffle.io.maxRetries*spark.shuffle.io.retryWait
3 Yarn队列资源不足导致的application直接失败
3.1 问题描述:
如果yarn上的spark作业已经消耗了一部分资源,如果现在再提交一个spark作业,可能会出现两种情况:
第一、发现yarn资源不足,直接打印fail的log,直接就失败。
第二、发现yarn资源不足,该作业就一直处于等待状态,等待分配资源执行。
3.2 解决方案:
如果发生了上面的第一种问题,可以通过以下方法解决
方法一、在代码中限制同一时间内只有一个spark作业提交到yarn上,确保spark作业的资源是充足的(调节同一时间内每个spark能充分使用yarn的最大资源)。
方法二、将长时间的spark作业和短时间的spark作业分别提交到不同的队列中(通过线程池的方式实现)。
4 各种序列化导致的报错
4.1 问题描述:
用client模式去提交spark作业,观察本地打印出来的log。如果出现了类似于Serializable、Serialize等等字眼,报错的log,那么恭喜大家,就碰到了序列化问题导致的报错。
4.2 解决方案:
1、你的算子函数里面,如果使用到了外部的自定义类型的变量,那么此时,就要求你的自定义类型,必须是可序列化的。
2、如果要将自定义的类型,作为RDD的元素类型,那么自定义的类型也必须是可以序列化的。
3、不能在上述两种情况下,去使用一些第三方的,不支持序列化的类型。
5 解决算子函数返回Null导致的问题
5.1 问题描述:
1、如果碰到某些值,不想要有返回值的时候
5.2 解决方案:
1、在返回的时候,返回一些特殊的值,不要返回null,比如“-999”
2、在通过算子获取到了一个RDD之后,可以对这个RDD执行filter操作,进行数据过滤。filter内,可以对数据进行判定,如果是-999,那么就返回false,给过滤掉就可以了。
3、大家不要忘了,之前咱们讲过的那个算子调优里面的coalesce算子,在filter之后,可以使用coalesce算子压缩一下RDD的partition的数量,让各个partition的数据比较紧凑一些。也能提升一些性能。
6 解决yarn-client模式导致的网卡流量激增问题
6.1 问题描述:
由于咱们的driver是启动在本地机器的,而且driver是全权负责所有的任务的调度的,也就是说要跟yarn集群上运行的多个executor进行频繁的通信(中间有task的启动消息、task的执行统计消息、task的运行状态、shuffle的输出结果)。
在整个spark运行的生命周期内,都会频繁的去进行通信和调度。所有这一切通信和调度都是从你的本地机器上发出去的,和接收到的。那么此时,你的本地机器的网络通信负载是非常非常高的。会导致你的本地机器的网卡流量会激增!!!
6.2 解决方案:
yarn-client模式,通常咱们就只会使用在测试环境中。实际上线了以后,在生产环境中,都得用yarn-cluster模式,去提交你的spark作业。
yarn-cluster模式,就跟你的本地机器引起的网卡流量激增的问题,就没有关系了。使用了yarn-cluster模式以后,就不是你的本地机器运行Driver,进行task调度了。是yarn集群中,某个节点会运行driver进程,负责task调度。
7 yarn-cluster模式的JVM栈内存溢出问题
7.1 问题描述:
实践经验,碰到的yarn-cluster的问题:
有的时候,运行一些包含了spark sql的spark作业,可能会碰到yarn-client模式下,可以正常提交运行;yarn-cluster模式下,可能是无法提交运行的,会报出JVM的PermGen(永久代)的内存溢出,OOM。
7.2 问题原因:
yarn-client模式下,driver是运行在本地机器上的,spark使用的JVM的PermGen的配置,是本地的spark-class文件(spark客户端是默认有配置的),JVM的永久代的大小是128M,这个是没有问题的;但是呢,在yarn-cluster模式下,driver是运行在yarn集群的某个节点上的,使用的是没有经过配置的默认设置(PermGen永久代大小),82M。
spark-sql,它的内部是要进行很复杂的SQL的语义解析、语法树的转换等等,特别复杂,在这种复杂的情况下,如果说你的sql本身特别复杂的话,很可能会比较导致性能的消耗,内存的消耗。可能对PermGen永久代的占用会比较大。会报出PermGen Out of Memory error log。
7.3 解决方案:
既然是JVM的PermGen永久代内存溢出,那么就是内存不够用。咱们呢,就给yarn-cluster模式下的,driver的PermGen多设置一些。
spark-submit脚本中,加入以下配置即可:
--conf spark.driver.extraJavaOptions="-XX:PermSize=128M -XX:MaxPermSize=256M"
这个就设置了driver永久代的大小,默认是128M,最大是256M。那么,这样的话,就可以基本保证你的spark作业不会出现上述的yarn-cluster模式导致的永久代内存溢出的问题。
8 错误的持久化方式以及checkpoint的使用
8.1 checkpoint的作用:
默认持久化的Rdd会保存到内存或磁盘中,下次使用该Rdd时直接冲缓存中获取,不需要重新计算;如果内存或者磁盘中文件丢失,再次使用该Rdd时需要重新进行。
如果将持久化的Rdd进行checkpoint处理,会把内存写入到hdfs文件系统中,此时如果再次使用持久化的Rdd,但文件丢失后,会从hdfs中获取Rdd并重新进行缓存。
8.2 正确使用:
首先设置checkpoint目录
javaSparkContext.checkpointFile("hdfs://hadoopName:8020/user/yanglin/spark/checkpoint/UserVisitSessionAnalyzeSpark");
将缓存后的Rdd进行checkpoint处理
sessionRowPairRdd.checkpoint();