原因分析
参考Spark RSS(Remote Shuffle Service),它存在的目的就是解决Spark Shuffle FetchFailedException这类问题。比如参考 降本增效利器!趣头条Spark Remote Shuffle Service最佳实践
- Spark Shuffle Fetch过程存在大量的网络小包,现有的External Shuffle Service设计并没有非常细致的处理这些RPC请求,大规模场景下会有很多connection reset发生,导致FetchFailed,从而导致stage重算。
- Spark Shuffle Fetch过程存在大量的随机读,大规模高负载集群条件下,磁盘IO负载高、CPU满载时常发生,极容易发生FetchFailed,从而导致stage重算。
重算过程会放大集群的繁忙程度,抢占机器资源,导致恶性循环严重,SLA完不成,需要运维人员手动将作业跑在空闲的Label集群。 - 计算和Shuffle过程架构不能拆开,不能把Shuffle限定在指定的集群内,不能利用部分SSD机器。
- M*N次的shuffle过程:对于10K mapper,5K reducer级别的作业,基本跑不完。
- NodeManager和Spark Shuffle Service是同一进程,Shuffle过程太重,经常导致NodeManager重启,从而影响Yarn调度稳定性。
Shuffle 解决办法
流传比较广的一篇解析 【Spark系列8】Spark Shuffle FetchFailedException报错解决方案
我实际是遇到过Spark Shuffle FetchFailedException,但是任务可以运行过去,所以以下方案都没用过,只是来看看备案。
- 减少shuffle数据
思考是否可以使用map side join或是broadcast join来规避shuffle的产生。
将不必要的数据在shuffle前进行过滤,比如原始数据有20个字段,只要选取需要的字段进行处理即可,将会减少一定的shuffle数据。
- SparkSQL和DataFrame的join,group by等操作(提供shuffle并发度)
通过spark.sql.shuffle.partitions控制分区数,默认为200,根据shuffle的量以及计算的复杂度提高这个值。
- Rdd的join,groupBy,reduceByKey等操作
通过spark.default.parallelism控制shuffle read与reduce处理的分区数,默认为运行任务的core的总数(mesos细粒度模式为8个,local模式为本地的core总数),官方建议为设置成运行任务的core的2-3倍。
- 提高executor的内存
通过spark.executor.memory适当提高executor的memory值
- 是否存在数据倾斜的问题
空值是否已经过滤?某个key是否可以单独处理?考虑改变数据的分区规则。