Spark trouble shooting

yarn-client模式下导致的网卡流量激增

yarn-client模式下,会产生什么样的问题呢?

由于咱们的driver是启动在本地机器的,而且driver是全权负责所有的任务的调度的,
也就是说要跟yarn集群上运行的多个executor进行频繁的通信(中间有task的启动消息、
task的执行统计消息、task的运行状态、shuffle的输出结果)。

咱们来想象一下。比如你的executor有100个,stage有10个,task有1000个。每个stage运行的时候,
都有1000个task提交到executor上面去运行,平均每个executor有10个task。接下来问题来了,
driver要频繁地跟executor上运行的1000个task进行通信。通信消息特别多,通信的频率特别高。
运行完一个stage,接着运行下一个stage,又是频繁的通信。

在整个spark运行的生命周期内,都会频繁的去进行通信和调度。所有这一切通信和调度都是从你的
本地机器上发出去的,和接收到的。这是最要人命的地方。你的本地机器,很可能在30分钟内
(spark作业运行的周期内),进行频繁大量的网络通信。那么此时,你的本地机器的网络通信负载
是非常非常高的。会导致你的本地机器的网卡流量会激增!!!

你的本地机器的网卡流量激增,当然不是一件好事了。因为在一些大的公司里面,对每台机器的使用情况,
都是有监控的。不会允许单个机器出现耗费大量网络带宽等等这种资源的情况。运维人员不会允许。
可能对公司的网络,或者其他(你的机器还是一台虚拟机),如果你是一台虚拟机的话,和其他机器
共享网卡的话,可能对其他机器,公司整个网络环境,都会有负面和恶劣的影响。

解决的方法:

实际上解决的方法很简单,就是心里要清楚,yarn-client模式是什么情况下,可以使用的?
yarn-client模式,通常咱们就只会使用在测试环境中,你写好了某个spark作业,打了一个jar包,
在某台测试机器上,用yarn-client模式去提交一下。因为测试的行为是偶尔为之的,
不会长时间连续提交大量的spark作业去测试。还有一点好处,yarn-client模式提交,
可以在本地机器观察到详细全面的log。通过查看log,可以去解决线上报错的故障(troubleshooting)、
对性能进行观察并进行性能调优。

实际上线了以后,在生产环境中,都得用yarn-cluster模式,去提交你的spark作业。
yarn-cluster模式,就跟你的本地机器引起的网卡流量激增的问题,就没有关系了。也就是说,
就算有问题,也应该是yarn运维团队和基础运维团队之间的事情了。
他们去考虑Yarn集群里面每台机器是虚拟机还是物理机呢?网卡流量激增后会不会对其他东西产生影响呢?
如果网络流量激增,要不要给Yarn集群增加一些网络带宽等等这些东西。那就是他们俩个团队的事情了,
和你就没有关系了

使用了yarn-cluster模式以后,
就不是你的本地机器运行Driver,进行task调度了。是yarn集群中,某个节点会运行driver进程,
负责task调度。

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

在算子函数中,返回null

return actionRDD.mapToPair(new PairFunction() {

    private static final long serialVersionUID = 1L;

    @Override
    public Tuple2 call(Row row) throws Exception {
        return new Tuple2("-666", RowFactory.createRow("-999"));  
         return null
    }       
});

大家可以看到,在有些算子函数里面,是需要我们有一个返回值的。但是,有时候,我们可能对某些值,
就是不想有什么返回值。
我们如果直接返回NULL的话,会报错的!!!

如果碰到你的确是对于某些值,不想要有返回值的话,有一个解决的办法:

1、在返回的时候,返回一些特殊的值,不要返回null,比如“-999”
2、在通过算子获取到了一个RDD之后,可以对这个RDD执行filter操作,进行数据过滤。
filter内,可以对数据进行判定,如果是-999,那么就返回false,给过滤掉就可以了。
3、大家不要忘了,之前咱们讲过的那个算子调优里面的coalesce算子,在filter之后,
可以使用coalesce算子压缩一下RDD的partition的数量,让各个partition的数据比较紧凑一些。
也能提升一些性能。

解决各种序列化导致的错误

你会看到什么样的序列化导致的报错?

用client模式去提交spark作业,观察本地打印出来的log。如果出现了类似于Serializable、
Serialize等等字眼,报错的log,那么恭喜大家,就碰到了序列化问题导致的报错。

虽然是报错,但是序列化报错,应该是属于比较简单的了,很好处理。

序列化报错要注意的点:

1、你的算子函数里面,如果使用到了外部的自定义类型的变量,那么此时,就要求你的自定义类型,
必须是可序列化的。

2、如果要将自定义的类型,作为RDD的元素类型,那么自定义的类型也必须是可以序列化的

JavaPairRDD

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

实践经验,碰到的yarn-cluster的问题:

有的时候,运行一些包含了spark sql的spark作业,可能会碰到yarn-client模式下,可以正常提交运行;
yarn-cluster模式下,可能是无法提交运行的,会报出JVM的PermGen(永久代)的内存溢出,OOM。
JVM里面的一个区域,就是会放Class里面一些字符串常量这些东西的。

yarn-client模式下,driver是运行在本地机器上的,spark使用的JVM的PermGen的配置,
是本地的spark-class文件(spark客户端是默认有配置的),JVM的永久代的大小是128M,
这个是没有问题的;但是呢,在yarn-cluster模式下,driver是运行在yarn集群的某个节点上的,
使用的是没有经过配置的默认设置(PermGen永久代大小),82M。

spark-sql,它的内部是要进行很复杂的SQL的语义解析、语法树的转换等等,特别复杂,
在这种复杂的情况下,如果说你的sql本身特别复杂的话,很可能会比较导致性能的消耗,内存的消耗。
可能对PermGen永久代的占用会比较大。

所以,此时,如果对永久代的占用需求,超过了82M的话,但是呢又在128M以内;就会出现如上所述的问题,
yarn-client模式下,默认是128M,这个还能运行;如果在yarn-cluster模式下,默认是82M,就有问题了。
会报出PermGen Out of Memory error log。

如何解决这种问题?

既然是JVM的PermGen永久代内存溢出,那么就是内存不够用。咱们呢,就给yarn-cluster模式下的,
driver的PermGen多设置一些。

spark-submit脚本中,加入以下配置即可:
–conf spark.driver.extraJavaOptions=”-XX:PermSize=128M -XX:MaxPermSize=256M”

这个就设置了driver永久代的大小,默认是128M,最大是256M。那么,这样的话,
就可以基本保证你的spark作业不会出现上述的yarn-cluster模式导致的永久代内存溢出的问题。

多讲一句,可能还有一个问题
spark sql,sql,要注意,一个问题

sql,有大量的or语句。比如where keywords=” or keywords=” or keywords=”
当达到or语句,有成百上千的时候,此时可能就会出现一个driver端的jvm stack overflow,
JVM栈内存溢出的问题

JVM栈内存溢出,基本上就是由于调用的方法层级过多,因为产生了大量的,非常深的,
超出了JVM栈深度限制的,递归。递归方法。我们的猜测,spark sql,有大量or语句的时候,
spark sql内部源码中,在解析sql,比如转换成语法树,或者进行执行计划的生成的时候,
对or的处理是递归。or特别多的话,就会发生大量的递归。

JVM Stack Memory Overflow,栈内存溢出。

这种时候,建议不要搞那么复杂的spark sql语句。采用替代方案:将一条sql语句,
拆解成多条sql语句来执行。每条sql语句,就只有100个or子句以内;一条一条SQL语句来执行。
根据生产环境经验的测试,一条sql语句,100个or子句以内,是还可以的。通常情况下,
不会报那个栈内存溢出。

你可能感兴趣的:(spark)