Spark学习笔记(12)---troubleshooting
troubleShooting
- troubleShooting
* 控制shuffle reduce端缓冲大小以避免OOM
* JVM GC导致shuffle文件拉取失败
* Yarn队列资源不足导致Application直接失败
* 各种序列化导致的报错
* 算子函数返回NULL导致的问题
* Yarn-cluster模式JVM内存溢出无法执行
控制shuffle reduce端缓冲大小以避免OOM
有的时候,map端的数据量特别大,然后写出的速度特别快。reduce端所有task,拉取的时候,全部达到自己的缓冲的最大极限值,48M,全部填满。这个时候,再加上你的reduce端执行的聚合函数的代码,可能会创建大量的对象。
然后,内存就撑不住了,就会OOM。reduce端的内存中,就会发生内存溢出的问题。
这个时候,就应该减少reduce端task缓冲的大小。我宁愿多拉取几次,但是每次同时能够拉取到reduce端每个task的数量,比较少,就不容易发生OOM内存溢出的问题。(比如,可以调节成12M)
JVM GC导致shuffle文件拉取失败
这个问题在JVM调优之调节Executor堆外内存与连接等待时长中提到过, 其中只讲了连接等待的时间,这里还可以配置两个参数
spark.shuffle.io.maxRetries 3
spark.shuffle.io.retryWait 5s
第一个参数,意思就是说,shuffle文件拉取的时候,如果没有拉取到(拉取失败),最多或重试几次(会重新拉取几次文件),默认是3次。
第二个参数,意思就是说,每一次重试拉取文件的时间间隔,默认是5s钟。
Yarn队列资源不足导致Application直接失败
不同执行时间提交到不同的队列中去等待,可以通过线程池(一个线程池对应一个资源队列)的方法来实现。
各种序列化导致的报错
如果日志文件中出现了类似于Serializable、Serialize等等报错的log,那就是出现了序列化报错。
序列化报错要注意的三个点:
(1) 你的算子函数里面,如果使用到了外部的自定义类型的变量,那么此时,就要求你的自定义类型,必须是可序列化的。
final Teacher teacher = new Teacher("leo");
studentsRDD.foreach(new VoidFunction() {
public void call(Row row) throws Exception {
String teacherName = teacher.getName();
....
}
});
public class Teacher implements Serializable {
}
(2) 如果要将自定义的类型,作为RDD的元素类型,那么自定义的类型也必须是可以序列化的
JavaPairRDD<Integer, Teacher> teacherRDD
JavaPairRDD<Integer, Student> studentRDD
studentRDD.join(teacherRDD)
public class Teacher implements Serializable {
}
public class Student implements Serializable {
}
(3) 不能在上述两种情况下,去使用一些第三方的,不支持序列化的类型
Connection conn =
studentsRDD.foreach(new VoidFunction() {
public void call(Row row) throws Exception {
conn.....
}
});
Connection是不支持序列化的
算子函数返回NULL导致的问题
在有些算子函数里面,是需要我们有一个返回值的。但是,有时候,我们可能对某些值,就是不想有什么返回值。我们如果直接返回NULL的话,那么可以不幸的告诉大家,是不行的,会报错的。
- 解决办法
- 在返回的时候,返回一些特殊的值,不要返回null,比如“-999”
- 在通过算子获取到了一个RDD之后,可以对这个RDD执行filter操作,进行数据过滤。filter内,可以对数据进行判定,如果是-999,那么就返回false,给过滤掉就可以了。
- 在filter之后,可以使用coalesce算子压缩一下RDD的partition的数量,让各个partition的数据比较紧凑一些。也能提升一些性能。
Yarn-cluster模式JVM内存溢出无法执行
有的时候,运行一些包含了spark sql的spark作业,可能会碰到yarn-client模式下,可以正常提交运行;yarn-cluster模式下,可能是无法提交运行的,会报出JVM的PermGen(永久代)的内存溢出,OOM。
yarn-client模式下,driver是运行在本地机器上的,spark使用的JVM的PermGen的配置,是本地的spark-class文件(spark客户端是默认有配置的),JVM的永久代的大小是128M,这个是没有问题的;但是呢,在yarn-cluster模式下,driver是运行在yarn集群的某个节点上的,使用的是没有经过配置的默认设置(PermGen永久代大小),82M。
spark-sql,它的内部是要进行很复杂的SQL的语义解析、语法树的转换等等,特别复杂,在这种复杂的情况下,如果说你的sql本身特别复杂的话,很可能会比较导致性能的消耗,内存的消耗。可能对PermGen永久代的占用会比较大。
spark-submit脚本中,加入以下配置即可:
--conf spark.driver.extraJavaOptions="-XX:PermSize=128M -XX:MaxPermSize=256M"
Tips:
Spark SQL,有大量的or语句。比如where keywords='' or keywords='' or keywords=''
当达到or语句,有成百上千的时候,此时可能就会出现一个driver端的jvm stack overflow,JVM栈内存溢出的问题。
因此可以吧sql语句拆分成多条sql语句来执行
参考资料
《北风网Spark项目实战》
github: https://github.com/yangtong123/StudySpark