spark任务shuffle溢出报错处理: Missing an output location for shuffle 0

10 篇文章 0 订阅

执行数据量较大的spark任务时经常会出现MetadataFetchFailedException:

org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 10
at org.apache.spark.MapOutputTracker$$anonfun$convertMapStatuses$2.apply(MapOutputTracker.scala:867)
at org.apache.spark.MapOutputTracker$$anonfun$convertMapStatuses$2.apply(MapOutputTracker.scala:863)
at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:733)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
原因分析:

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。

解决思路:
  1. 减少shuffle数据
    主要从代码层面着手,可以将不必要的数据在shuffle前进行过滤,比如原始数据有20个字段,只要选取需要的字段进行处理即可,将会减少一定的shuffle数据。
  2. 修改分区
    通过spark.sql.shuffle.partitions控制分区数,默认为200,根据shuffle的量以及计算的复杂度适当提高这个值,例如500。
  3. 增加失败的重试次数和重试的时间间隔
    通过spark.shuffle.io.maxRetries控制重试次数,默认是3,可适当增加,例如10。
    通过spark.shuffle.io.retryWait控制重试的时间间隔,默认是5s,可适当增加,例如10s。
  4. 提高executor的内存
    在spark-submit提交任务时,适当提高executor的memory值,例如15G或者20G。
  5. 考虑是否存在数据倾斜的问题

另外可参考其他的优化方式点击此文章

自我实践总结

这么长时间感觉遇到这个问题都是发生数据倾斜造成,分享一次遇到的成功处理经验:

业务场景

在做学生知识点画像时,基础数据量大且热点数据多,遇到事实表关联(left join)维度表做数据处理,多条件关联时热点学校和热点知识点造成数据倾斜

解决方法

利用多主键(也就是left join条件),塑造唯一主键细化数据粒度,使得数据一一对应

示例

文字描述看不懂我们直接上代码
knowledge_level表(数据量在7000w+):

school_idpaper_idknowledge_id
0011001100001xxxx
0021011100001xxxx

base_school_paper表(数据量在300w+):

school_idpaper_idgradepositionsubject
0011001一年级中国山东数学
0021011三年级中国北京物理
数据倾斜方案

发生数据倾斜的代码

select
   school_id,
   paper_id,
   knowledge_id,
   ....
from knowledge_level knowledge
left join base_school_paper school
on knowledge.school_id = school.schoo_id
and knowledge.paper_id = school.paper_id

处理过后的代码

select
   knowledge.school_id AS school_id,
   knowledge.paper_id AS paper_id,
   knowledge.knowledge_id AS knowledge_id,
   ....
from (
   select 
       school_id,
       paper_id,
       knowledge_id,
       concat(school_id,'_',paper_id) AS school_paper_id
       ...
   from knowledge_level
) knowledge
left join (
  select
     school_id,
     paper_id,
     knowledge_id,
     concat(school_id,'_',paper_id) AS school_paper_id 
     ....
  from base_school_paper
) school
on knowledge.school_paper_id = school.school_paper_id
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扫地增

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值