欢迎光临
我们一直在努力

Hadoop Reduce OOM 真实排查案例(Hive On MapReduce)

本质:脏数据 → 数据倾斜 → OOM

业务背景:用户行为日志每日离线跑批,Hive 执行 group by user_id 统计用户行为次数。每日跑批,某天作业突然失败,Reduce 任务 OOM,绝大多数 task 很快跑完,仅 1 个 reduce 反复重试后 OOM 失败。报错日志:

java.lang.OutOfMemoryError: Java heap space
Container killed by YARN for exceeding memory limits.
AttemptID:attempt_xxxx_r_000012,多次重试失败,退出码143

区分:

  • 单纯调大内存只是临时掩盖;
  • 本次根因:上游埋点采集 bug 产生脏数据,大量记录 user_id 字段输出同一个异常脏 key(字符串 -99999),全部 shuffle 到同一个 reduce,把 reduce 堆内存撑爆。不是代码逻辑错误,是上游脏数据造成热点 key 倾斜,引发 reduce OOM。
  • 一、完整排查步骤

    步骤 1:YARN UI 观察现象,确认是 Reduce 侧问题

  • 打开 ResourceManager 页面,找到失败 Job;看 Job 概览:Map 100% 全部完成,Reduce 卡在 99%,只有一个 reduce task 反复失败,其余几十个 reduce 几秒全部跑完。
  • 打开 Counters → Map-Reduce Framework → Reduce input records。
    • 正常 reduce:每个输入记录 2~5 万条。
    • 失败的 reduce12:输入记录 1200 万条,相差几百倍。👉 确认发生数据倾斜,问题出在这个 reduce。
  • Counter 只能看到数量,看不到具体 key。

    步骤 2:定位异常 Reduce 对应的 Container,下载 task 日志

  • Job 页面找到失败的 reduce taskID r_000012,点开 Attempts,拿到 ContainerID。
  • 跳转该 NodeManager 节点,下载 reduce task 日志(syslog/stderr)。
  • 日志特征:大量频繁 Full GC,GC 时间占比极高;大量重复打印同一个 key 值:user_id=-99999。
  • 此时定位热点脏 key:‑99999。

    步骤 3:回查原始 Hive 表,验证脏数据

    执行 SQL 统计 key 分布,确认脏 key 的量级:

    select user_id,count(*) as cnt
    from dwd_user_log
    group by user_id
    order by cnt desc
    limit 5;

    输出:

    表格

    user_idcnt
    -99999 1180 万
    135xxxx 2.3 万
    136xxxx 2.1 万

    确认:上游埋点服务 bug,部分设备上报时用户 ID 解析失败,统一填默认值‑99999,产生千万级脏 key,shuffle 全部落到同一个 reduce,reduce 迭代器把海量 value 加载进内存,直接堆溢出 OOM。

    注意:不是业务正常数据,属于脏数据。

    步骤 4:临时应急方案(先让任务跑过,保障调度)

    方案 1:过滤脏 key(业务允许),SQL 增加 where 条件

    select user_id,count(1)
    from dwd_user_log
    where user_id != '-99999' –过滤脏数据
    group by user_id;

    临时应急,任务直接跑通。

    方案 2:如果业务不能直接过滤,对脏 key 做加盐打散

    select
    if(user_id='-99999',concat(user_id,'_',floor(rand()*10)),user_id) as user_id_salt,
    count(1)
    from dwd_user_log
    group by if(user_id='-99999',concat(user_id,'_',floor(rand()*10)),user_id)

    把脏 key 打散到 10 个 reduce,避免单 reduce 过载;后续再做聚合合并结果。

    方案 3:开启 hive.groupby.skewindata=true,拆两轮 MR 做自动倾斜处理(消耗更多资源)。

    ❗不要优先只调大 reduce 内存:mapreduce.reduce.memory.mb。就算调到 8G,脏数据量继续涨,早晚还会 OOM,治标不治本。

    步骤 5:根治源头(防止次日再次发生)

  • 通知埋点开发修复采集 bug,不再生成‑99999异常 user_id。
  • 在数仓 DWD 层增加数据清洗逻辑,过滤 / 标记该脏 key;增加监控告警:监控 user_id 分布,某一个 key 行数超过阈值就告警。
  • 二、关键点

  • 为什么同一个 key 千万条记录会把 reduce OOM?
  • MR reduce 函数接收 (key, Iterable<value>),shuffle 拉取过来该 key 全部的 value;虽然 Iterable 是迭代读取,但 shuffle 合并阶段大量数据会压入内存缓冲区;当同一个 key 千万条,内存缓冲区撑爆,就报 Java heap space OOM。

  • 调大 reduce 内存能不能彻底解决?
  • 不能。脏数据量持续增长,再大内存也会被打满;调内存只能临时续命,根本要处理脏数据 / 热点 key。

  • 怎么区分:是业务正常热点 key,还是脏数据热点 key?
  • 查询原始表,看该 key 业务含义;如果是异常默认值、null、异常编码,就是脏数据;如果是真实业务对象(爆款商品、头部大用户)属于业务热点。

  • Counter 和日志分工再回顾
  • Counter 看每个 reduce 输入记录数,发现倾斜现象;下载对应 Container 的 task 日志拿到真实热点 key。

    三、精简版

    线上遇到 Hive on MR 的 reduce OOM。现象是 map 全部跑完,大部分 reduce 快速结束,只有一个 reduce 反复重试 OOM。

    首先去 YARN UI 看 job 的 Counter 中 Reduce‑input‑records,发现单个 reduce 输入千万条,远大于其他 reduce,确认倾斜。

    找到失败 reduce 对应的 Container,下载 task 日志,发现大量重复 key‑99999。

    回查源表统计 key 分布,确认是上游埋点 bug 产生脏数据,千万条记录 user_id 等于‑99999,shuffle 全部落到同一个 reduce,造成 OOM。

    应急在 SQL 过滤脏 key 先保障调度跑通;

    同时通知上游修复埋点,DWD 层增加清洗逻辑和分布监控,从源头解决。

    没有单纯调大 reduce 内存,因为调内存只是临时掩盖问题。

    四、延伸:Spark 版本同类现象

    Spark 中对应现象:Spark UI 某个 Stage,少数 task 的 Shuffle Read 几百 MB‑GB 级别,Executor 报 OOM;

    排查思路:看 task 的 Shuffle Read → 找到慢 task 对应 Executor 日志 → SQL 统计 key 分布定位脏 key。

    容易踩坑点

  • 看到 OOM 就直接加大 YARN 容器内存,不去查数据分布,任务后续还会失败。
  • 分不清:容器被 YARN kill,有两种情况:JVM 堆 OOM;物理内存超限(堆外 + 进程占用),要区分日志信息阿里云帮助…。
  • 脏数据不只是‑99999,还有大量 null、空字符串、异常编码,都会造成热点倾斜 OOM。
  • 赞(0)
    未经允许不得转载:171主机测评 » Hadoop Reduce OOM 真实排查案例(Hive On MapReduce)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址