巡检任务发现,今天有个任务报错了,一看没有修改过,先重启了,过了几分钟又报错了~
emo…
然后检查日志,发现报错了:
missing an output location for shuffle 0:
。。。。。。。。。。。。。。。。。。。。。。
1
2
于是查了下,发现是任务spark.sql.shuffle.partitions设置的是200,该任务数据量大,于是调大了 spark.sql.shuffle.partitions 为400,又提高executor的memory值,再次重启发现很快执行成功了,执行时间是历史执行成功时间的50%,还提高了效率~
针对该问题,总结一下~
原因分析:
shuffle分为shuffle write和shuffle read两部分。
shuffle write的分区数由上一阶段的RDD分区数控制,shuffle read的分区数则是由Spark提供的一些参数控制。
shuffle write可以简单理解为类似于saveAsLocalDiskFile的操作,将计算的中间结果按某种规则临时放到各个executor所在的本地磁盘上。
shuffle read的时候数据的分区数则是由spark提供的一些参数控制。可以想到的是,如果这个参数值设置的很小,同时shuffle read的量很大,那么将会导致一个task需要处理的数据非常大。结果导致JVM crash,从而导致取shuffle数据失败,同时executor也丢失了,看到Failed to connect to host的错误,也就是executor lost的意思。有时候即使不会导致JVM crash也会造成长时间的gc。
解决思路:
减少shuffle数据
主要从代码层面着手,可以将不必要的数据在shuffle前进行过滤,比如原始数据有20个字段,只要选取需要的字段进行处理即可,将会减少一定的shuffle数据。
修改分区
通过spark.sql.shuffle.partitions控制分区数,默认为200,根据shuffle的量以及计算的复杂度适当提高这个值,例如400。
提高executor的内存
在spark-submit提交任务时,适当提高executor的memory值,例如5G或者10G。