面向 P6-P7+ 工程师 框架训练型 · 问题现场 约 8,900–9,500 字 信息截止 2026-08

大数据任务排障实战:
YARN 抢占、Hive 卡顿与数据倾斜

把「凌晨告警、任务反复 KILL、99% 进度卡死」拆成可复现的排查链路。 用统一 SOP 把资源层、执行层、数据层三类故障一次定位,而不是在改 Hive 参数与重跑之间碰运气。

主线风格:框架训练 45% + 问题现场 45% + 推导 10% 版本假设:Hadoop 3.3.x(Capacity Scheduler)· Hive 3.1.x · Tez 证据等级:官方文档优先,标注推断与生产经验
问题现场 · 复合场景

102:17 三条告警:倾斜还是抢占

复合场景 · 综合多家离线数仓值班复盘的典型模式,非指代具体单一事故 TYPICAL SCENARIO

凌晨 02:17,值班群连续弹出三条告警:日批任务 ads_user_trade_daily 第三次失败; YARN 队列 root.batch 资源占用 98%;同时 root.adhoc 里分析师提交的即席查询全部 PENDING。 值班同学第一反应是「倾斜了」,改了几个 Hive 参数重跑,任务在 Reduce 阶段跑了 40 分钟后被 KILL, 日志里只有一行 Application killed by preemption

这是复合场景(虚构,用于教学):表面现象是 Hive 跑不完,根因却横跨 YARN 队列配置、 Tez Shuffle 阻塞与 Join Key 倾斜三层。线上真实事故往往也是多因素叠加——只盯 SQL 或只盯队列, 都会把排查拖成「反复重跑、碰运气」。

事后推演:若把 preempt 日志与 Tez 最慢 task 时间线画在同一张纸上,通常能在十五分钟内确认是 「倾斜拉长作业 → 触发抢占」还是「纯资源不足」。本篇就是把这套手工方法沉淀成文档化 SOP。

本篇假设读者已掌握 HDFS / YARN / Hive 基础(可参考同系列 T01、T02),聚焦排障框架全链路 SOP:先建立「现象分类 → 工具链 → 根因 → 处置 → 预防」的不变量, 再用两个复合案例走完整复盘。分享约 45 分钟:20 分钟框架与 YARN / Hive 分诊,20 分钟双案例 walkthrough, 5 分钟 SOP 与 Runbook 讨论。

开始敲命令之前,先确认手上有四类信息:YARN applicationId 与队列名、Hive SQL 或脚本版本、 任务开始 / 失败时间窗、Tez DAG 截图或 History Server 链接。缺任一项都容易误判——例如没有队列名就无法对照 capacity 配置,没有精确时间窗就无法和 RM 审计日志对齐。

值班现场最常见的失败模式不是「不会查日志」,而是层次选错:看到进度条卡在 99% 就去改 hive.exec.reducers.max;看到 Application KILLED 就去扩集群;看到 Shuffle 慢就去加带宽。 这些动作各自在单层故障里可能有效,但在复合场景里往往把窗口浪费在症状上。本篇刻意把「分层次」写成可执行口令: 任何告警的第一分钟只做一件事——读 YARN 终态与 Diagnostics,再决定走资源、执行还是数据入口。

另一个隐性成本是交接失真。夜班同学改完参数重跑「看起来好了」,白班接手时看不到 counter 基线与队列快照, 就会把偶然成功当成根因消除。因此后文所有 SOP 都要求把关键输出写进工单:应用终态、队列 Used/Max、 最慢 task 耗时、采样 TOP N。没有这四项,复盘会退化成「又挂了再改」。

层次管辖范围典型背锅点首选工具
资源层YARN 队列、NM 健康、Container 生命周期抢占、ACCEPTED 饥饿、Node 磁盘满yarn application、RM UI、capacity-scheduler.xml
执行层Tez DAG、Map / Shuffle / Reduce 并行度小文件 Map 爆炸、Spill 过多、Reducer 不足Tez UI、counter、EXPLAIN
数据层分区裁剪、Join Key 分布、脏数据热点 key、空值 / 0 值、维表膨胀采样 SQL、表统计信息、数据质量规则
常见误区

未确认 YARN 层就无限调大 hive.exec.reducers.max,会制造更多 Container,在抢占环境下适得其反。 未看 Tez 阶段就启用倾斜参数,可能对 Map 阶段问题无效。把「重跑成功」当「问题解决」而不留 counter 对比, 下次数据量上涨故障会复发。机制描述(Capacity Scheduler 抢占顺序、Tez 阶段划分)属于机制共识; 参数调优阈值与队列配比建议属于作者经验总结,落地前需结合集群规模与 SLA 验证。

后文章节安排:第二章给出三层排障地图;第三至五章分别展开 YARN 抢占、Hive 卡顿、倾斜识别框架; 第六、七章保全两个复合案例全链路;第八章划定「适合深挖 / 不适合先分析」边界;第九章沉淀统一 SOP; 第十章收束为决策矩阵与相邻知识地图。

体系总览 · 排障地图

2三层排障地图:先资源,后阶段,再数据

大数据离线任务排障有三条不变量。先分层次、再选工具:YARN preempt、Tez Shuffle、Join Key 倾斜 的证据入口不同,混在一起会浪费值班窗口。时间线对齐优于单点截图:KILL 时刻、首次 preempt、 最慢 Reduce 启动时刻必须画在同一轴上。缩短长尾优先于加资源:倾斜任务占用越多 Container、跑得越久, 越容易成为抢占 victim——只加节点或只调 max-capacity 往往治标。

图 1 · 资源 / 执行 / 数据三层排障地图
自制示意图
Hive / YARN 任务异常告警 现象分拣:先读 YARN 终态与 Diagnostics 资源层 preempt / ACCEPTED queue-status · capacity XML 时间窗隔离 · max-capacity 执行层 Map / Shuffle / Reduce Tez UI · counter · EXPLAIN grouping · sort buffer · MapJoin 数据层 热点 key / 脏 key 采样 TOP N · 空值桶 加盐 · 过滤 · ODS 规则 复合故障高频链路(本篇案例) 数据倾斜拉长 Reduce → 队列 Used 触顶 → 高优 pending 触发 preempt → Application KILLED 或:脏 Join Key → Shuffle 膨胀 → 网络热点 → 进度假死(终态仍 RUNNING) 处置顺序:缩短长尾 → 隔离资源 → 再调参 / 扩容
读图要点:分拣从 YARN Diagnostics 开始;复合故障按「数据根因 + 资源放大」理解,避免只杀队列或只改 SQL。

值班口诀可记为:「99% 看 Reduce,个位数看 Shuffle,均匀慢看 Map 或资源」。 若 Diagnostics 含 preemption,先走资源层六步命令链,再交叉 Tez 最慢 task;若终态 RUNNING 且 Shuffle 低位, 优先查 Join 输出与脏 key,而不是先加机器。

三层地图不是三套互斥手册,而是优先级与证据入口。资源层回答「为什么任务被杀死或根本起不来」; 执行层回答「活着的任务卡在哪一段」;数据层回答「为什么某一段异常长」。复合故障的典型形态是: 数据层制造长尾 → 执行层把长尾表现为进度假死或单 Task 99% → 资源层在队列触顶时把长作业选为 victim。 所以复盘时要能同时画出两条线:业务时间轴(提交、Reduce 启动、KILL)与资源时间轴(Used 贴 Max、pending 出现、preempt 事件)。

工具链选择也服从层次:RM UI / yarn application 属于资源层;Tez UI / counter / EXPLAIN 属于执行层;采样 SQL / 表统计 / 质量规则属于数据层。跨层抄工具会制造噪声——例如在资源层故障时去跑大规模 GROUP BY 采样,只会再烧一轮队列。正确做法是:资源层先用秒级命令确认是否 preempt;确认需要数据层证据时, 再用与失败任务相同分区谓词做小范围采样,而不是全表扫描。

核心机制 · YARN 抢占

3YARN 资源抢占:现象识别与队列排查

在 Capacity Scheduler 下,抢占(Preemption)是 RM 为更高优先级队列回收资源的机制,并非「随机杀任务」。 Hadoop 3.x 默认由 CapacityScheduler 的 PreemptionMonitor 周期扫描;当某 leaf 队列使用的资源超过其 max-capacity,且同父队列下有更高优先级 pending 应用时,RM 会向 NM 下发 preempt 指令。 排障时先把现象归入三类,避免与「任务本身慢」混淆。

现场沟通时建议统一术语:说「被抢占」时,要区分 Application 级 KILL 与 Container 级回收。 前者在 UI 上终态明确;后者可能表现为 task 失败重试、AM 重启、进度回退,Diagnostics 不一定立刻写出 preemption——此时必须用 yarn logs 检索 preempt 关键字,并与 RM Timeline 对齐, 否则会被误判成「Tez 不稳定」或「代码偶发 NPE」。

现象类别典型信号首要怀疑点
应用被直接 KILLYARN UI 状态 KILLED,Diagnostics 含 preemption跨队列优先级 / 队列 max-capacity 超限
Container 被回收AM 日志 Container preempted,任务重试或失败集群整体超卖、user-limit 或 leaf 队列无弹性
长期 ACCEPTED / 无法启动 AMyarn application -list 长时间 ACCEPTED队列 capacity 已满,非抢占但同属资源层

3.1 会读三层数字:capacity / maximum-capacity / user-limit-factor

排障队列问题时要会读三层数字capacity(保证容量占比)、 maximum-capacity(弹性上限)、user-limit-factor(单用户可占 leaf 队列比例)。 leaf 队列上的 RUNNING 应用消耗的是该 leaf 的份额;当 leaf 使用超过 max-capacity 且兄弟队列有 pending, 抢占监视器才会介入。Hive 3.x 提交任务时,队列来自 mapreduce.job.queuenametez.queue.name。若 HS2 默认队列与脚本不一致,会出现「以为在 batch 实际跑在 default」的错位—— 务必用 yarn application -status 核对实际 Queue 字段。

读配置时还要区分「保证」与「弹性」。capacity 描述的是父队列内的相对份额,不是绝对核数; maximum-capacity 才是该 leaf 在集群紧张时仍可冲到的上限。很多团队把 batch 的 max-capacity 设成 100, 意图是「凌晨没人时尽量吃满」,结果白天 adhoc 一旦 pending,监视器会把超份额的 batch Container 收回。 反过来,若 max-capacity 与 capacity 几乎相等,队列失去弹性,大促回刷会长期 ACCEPTED——这不是抢占, 但同属资源层饥饿,处置手段是临时上调 max 或独立 leaf,而不是去改 Hive SQL。

ordering-policy 与优先级语义同样关键。Capacity Scheduler 可按 FIFO 或 Fair 在 leaf 内排序; 跨队列抢占则更依赖父队列下的优先级配置与 pending 状态。排障时不要只盯「有没有 preempt 字样」, 还要问:被杀的是哪个 leaf、催命的是哪个兄弟队列、失败时刻 Used 是否已经越过 Max。 三问答齐,才能判断是「配置本就如此」还是「倾斜把作业拖进了抢占窗口」。

图 2 · Capacity Scheduler 抢占传导链
自制示意图
leaf Used > max-capacity 高优队列 有 pending 应用 RM 选 victim 占用多 · 运行久 NM 回收 通知 Tez AM 为何倾斜任务更容易被选为 victim 1. Reduce 长尾使作业运行时间落入 preempt 窗口之外,自然终止机会更少 2. 倾斜阶段 Container 数量多,单作业资源占用高,RM 回收收益更大 3. adhoc 与 batch 未做凌晨隔离时,pending 更容易催命核心日批 结论:抢占常是症状在资源层、病因可能在数据层——先缩短长尾,再谈队列弹性
机制要点:超限 + 高优 pending 同时成立才触发评估;倾斜满足「占用多、运行久」两个 victim 条件。
来源:Apache Hadoop 3.3 · Capacity Scheduler / Preemption(CapacityScheduler.html)。
已核验事实

Capacity Scheduler 的抢占由 ResourceManager 侧监视器驱动,目标是保障队列间公平与优先级语义, 而不是保证单个长作业永不被回收。官方文档明确队列通过 capacity / maximum-capacity 等属性描述份额与弹性上限; 具体 victim 选择与等待窗口受 yarn.resourcemanager.monitor.capacity.preemption.* 系列参数影响。 详见上述 Capacity Scheduler 文档。生产排障时,把「官方机制语义」与「本集群阈值经验」分开记录:前者可直接引用文档, 后者必须标注为经验值并在变更窗口复测,避免把别的集群的 wait/kill 毫秒数原样照搬。

3.2 抢占排查六步闭环

值班时按固定顺序执行,避免跳步导致误判。每一步产出应写入工单,便于跨班次交接。

步骤目的命令 / 操作判读要点
1. 锁定应用确认终态与队列归属yarn application -status <appId>State=KILLED 且 Diagnostics 含 preemption 才进入抢占链路;Queue 与脚本不一致则先修正提交配置
2. 对齐时间窗关联 RM 事件与业务高峰RM UI Timeline;yarn logs ... | grep -i preempt记录首次 preempt 与终态 KILL 间隔;间隔短说明 victim 选择果断,需优先缩短长尾
3. 快照队列还原失败时刻资源争用yarn queue -status 对照 batch / adhocUsed 持续贴 Max 且兄弟队列有 pending,抢占条件成立
4. 枚举 pending确认高优先级催命者yarn application -list -appStates ACCEPTED无 pending 却 preempt,需查配置不一致或误配置
5. 核对配置排除 XML 与热更新漂移核对 capacity-scheduler.xml 与 yarn-site 抢占开关确认 preemption.enabledmax_wait_before_kill、ordering-policy
6. 交叉 Tez区分纯抢占与倾斜诱发Tez UI 最慢 task;AM restart 次数最慢 Reduce 超 30 分钟且 Container 远高于均值,根因是倾斜,抢占只是放大器
# 查看应用终态与诊断信息(Hadoop 3.x)
yarn application -status application_1688888888888_0001 | grep -E 'State|Final|Diagnostics|Queue'
yarn queue -status root.batch.etl
yarn logs -applicationId application_xxx | grep -i preempt
来源:Apache Hadoop 3.3 · YARN Commands(YarnCommands.html)。

3.3 关键参数与处置优先级

参数名含义排障关联调优方向(经验)
yarn.resourcemanager.monitor.capacity.preemption.enabled是否开启容量抢占监视器false 时不会 preempt KILL,但 pending 可能永久饥饿生产通常 true;临时关闭仅作止血
...preemption.max_wait_before_kill标记可抢占后等待多久强制回收过小则长任务无完成机会;过大则高优等待过久批处理窗口可试 30000~60000 ms,需压测
...queue-path.capacity / maximum-capacity保证容量与弹性上限突破 max 且兄弟 pending 时触发评估大促临时上调 batch max,窗口结束回滚
...user-limit-factor单用户可占 leaf 比例倍数过大导致单用户独占诱发误伤多团队共享 leaf 时保持 1~2
mapreduce.job.queuename / tez.queue.name任务实际提交队列与脚本注释不一致时所有 queue 结论失效HS2 默认队列与批处理脚本双重校验
生产踩坑

user-limit-factor 设为很大(如 10)会让单用户占满 leaf 队列,其他团队任务进入 pending 后触发抢占「误伤」。 排查时务必同时看用户名 / 队列 / 时间窗三维。处置优先级:时间窗隔离 → 调 max-capacity → 调大 max_wait_before_kill → 配合 AM 重试;但单纯加 Hive 并行度可能加剧 Container 数量,更容易成为 victim。 监控建议接入 leaf 队列 Used/Max、每小时 preempt 计数、单 application 运行时长 P99(阈值需自校准)。

阶段YARN 抢占专项 SOP
现象任务失败或反复重试,Diagnostics 含 preemption;或非自身队列错误却 Container 丢失
工具yarn application -statusyarn queue -statusyarn logs、capacity-scheduler.xml
定位对比失败时刻队列使用率、max-capacity、pending 高优先级队列
方案时间窗隔离 / 调 max-capacity / 调抢占等待 / 降低单任务 Container 碎片
预防队列容量月度复盘;批处理与 adhoc 隔离;抢占事件接入监控大盘
核心机制 · Hive 卡顿

4Hive 任务卡顿:Map / Shuffle / Reduce 阶段定位

Hive 3.x 默认 Tez 引擎下,「卡住」必须精确到阶段。UI 上进度条长时间不动,可能是 Map 读 HDFS 慢、 Shuffle 网络 / 磁盘瓶颈、Reduce 倾斜或外部表锁表,处置手段完全不同。Tez 将 MR 的多 Job 链合并为 DAG, Vertex 之间通过 Edge 传递数据;因此「卡住」有时发生在上游 Vertex 已全部完成、下游尚未启动的调度间隙—— 这种短暂停顿不应与真正卡顿混淆,需看 Vertex 状态是 INIT 还是 RUNNING。CLI Stage 名与 Tez Vertex 不一一对应, 排障以 Tez UI 为准。

进度条本身是聚合视图,容易骗人。Hive CLI 或调度平台显示的「78%」往往把多个 Vertex 的完成比例做了加权; 真正有诊断价值的是:哪个 Vertex 占墙钟时间最多、该 Vertex 内 task 的 max/avg 比、以及该阶段的 Spill / HDFS_BYTES_READ / GC 是否相对历史基线异常。习惯「只看总进度」的值班同学,会在 Shuffle 假死与 Reduce 长尾 之间来回猜——两者在总进度上都可能表现为长时间不动,但对策完全相反。

还要注意「假卡顿」三类:一是编译阶段卡在 Metastore 锁或 CBO,Tez 根本未启动;二是 AM 刚被抢占重启, DAG 重新调度导致进度回退;三是外部依赖(JDBC 外表、远端 HTTP UDF)阻塞个别 task。 进入第四章后文的阶段分流前,先用「Tez 是否已有 RUNNING Vertex」把这三类剔掉,否则会误用 Map/Shuffle/Reduce 参数。

图 3 · Tez 阶段卡顿分流与首要怀疑点
自制示意图
Map 输入切分与读取 Shuffle 分区排序与溢写 Reduce 聚合或 Join 归并 怀疑:小文件 / 分区过宽 Map 数爆炸 · 单 Map 秒级 怀疑:Spill / 网络 / Join 输出 进度个位数 · Spill 倍增 怀疑:Key 倾斜 / GC 单 Task 99% · max >> avg 假像对照:均匀极慢 → 查资源或 Metastore;全局 AM restart → 回到抢占链路 口诀:99% 看 Reduce,个位数看 Shuffle,均匀慢看 Map 或资源
判读顺序:DAG 概览 → Vertex 耗时占比 → task max/avg → Counter → Diagnostics / Errors。
来源:Apache Hive 3.1 Language Manual · Configuration Properties(Hive Configuration Properties);Tez Runtime 配置见 Apache Tez 文档。

4.1 Tez UI 五步扫视与 Counter 触发线

  1. DAG 概览:Vertex 数量是否异常多(小文件)?是否有重复扫描同表?
  2. 各 Vertex 耗时占比:找出占总时长 50% 以上的阶段,后续只深查该阶段。
  3. Task 耗时分布:max 远大于 avg 十倍级,倾斜概率极高。
  4. Counter 面板:对比同作业历史基线,而不是绝对值。
  5. Diagnostics / Errors:是否有 OOM、Disk full 等硬错误。
Counter 名称异常信号(经验)可能含义
NUM_FAILED_TASKS持续增加且伴随 AM restart抢占、NM 故障或 OOM
SPILLED_RECORDS相对历史同 SQL 超 5 倍Shuffle 压力过大或 sort buffer 偏小
HDFS_BYTES_READ远高于表统计预估分区未裁剪、全表扫
REDUCE_INPUT_GROUPS单 task 远超 siblingsReduce 侧 key 倾斜
GC_TIME_MILLIS占 task 时间 30% 以上堆过小或单 task 数据量过大
-- Hive 3.1.x 排障 Session 参数示例(临时开启)
SET tez.grouping.min-size=67108864;
SET tez.grouping.max-size=268435456;
SET hive.exec.reducers.bytes.per.reducer=256000000;
SET hive.tez.auto.reducer.parallelism=true;
SET hive.task.progress=true;
推导要点

Tez 的 hive.exec.reducers.max 只是上限;真正并行度由输入大小与 bytes.per.reducer 决定。 遇到卡顿时先算「合理 Reduce 数 = 输入字节 / bytes.per.reducer」,再对比 UI 上是否出现单 Task—— 这是区分「资源不够」与「倾斜」的十秒判据。若合理 Reduce 数为 200 而 UI 显示 1 个 Task 极慢, 几乎可断定倾斜而非集群算力不足。部分遗留任务仍走 MapReduce,SOP 分层思路不变,仅工具入口不同。

Map 阶段还有一个高频误判:把「Map 数很多」直接等同于「小文件问题」。真正需要同时满足两件事: Map 数相对输入体积异常偏高,且单 Map 运行时间极短(秒级)。若 Map 数高但单 Map 也很长,更可能是分区谓词过宽或 单文件本身很大。处置前用 hdfs dfs -count 与表分区统计对照,避免一上来就调 tez.grouping.min-size 掩盖了全表扫描。

卡顿阶段关键信号根因类别快速验证
MapMap 数过大;单 Map 耗时极短小文件、分区扫描过宽hdfs dfs -count;收紧分区谓词
ShuffleSpill 高、Shuffle 占比过大数据量过大、sort buffer 不足tez.runtime.io.sort.mb;查 ORC 压缩
Reduce1 个 task 耗时为其他百倍Key 倾斜Tez task 分布;采样 key
全局频繁 AM restart、Container 骤降YARN 抢占RM Diagnostics 与时间线对齐
Metastore编译阶段卡住锁表、元数据慢查询SHOW LOCKS;Metastore 慢 SQL
核心机制 · 倾斜识别

5数据倾斜识别四步法

第六、七章都属于倾斜类问题,但表现阶段不同。进案例前,先统一倾斜识别四步法—— 这是框架训练的核心产出。T02 侧重建表、分区、Explain 与倾斜参数详解;本篇侧重值班现场如何把倾斜与 YARN / Hive 卡顿串联。两文互补:T02 学「怎么写对」,T04 学「怎么在告警里判对错」。

图 4 · 倾斜识别四步法与治理选型
自制示意图
1 确认倾斜 max > median×10 2 定位 key TOP N + 空值桶 3 选治理手段 加盐 / MapJoin / 过滤 4 验证回归 耗时 · preempt · 准确率 场景选型(首选 → 次选 → 不适用) GROUP BY 热点:两阶段聚合 + 加盐 → hive.groupby.skewindata → 仅调 reducer 数(不适用) Join 热点:MapJoin(可广播)→ Skew Join / 热点分离 → 盲目增大 sort buffer(不适用) 脏 key / 空值:ODS/DWD 过滤 → 单独无效分区 → 拖到 ADS 才过滤(不适用) 复合:倾斜 + 抢占:先 SQL 缩短长尾 → 再调队列 / 时间窗 → 只加集群节点(不适用)
注意:采样须与失败任务相同分区谓词;验证须看 EXPLAIN 是否真正出现 Map Join Operator。
-- 倾斜 key 采样模板(Hive 3.1.x)
SELECT join_key, COUNT(*) AS cnt
FROM your_table
WHERE dt = '${bizdate}'
GROUP BY join_key
ORDER BY cnt DESC
LIMIT 50;

在 Tez UI 查看同一 Vertex 下 task 耗时分布。若 max 耗时大于 median 的 10 倍,且最慢 task 的 REDUCE_INPUT_GROUPS(或 Map 输出 records)远高于其他 task,倾斜成立。若所有 task 耗时接近但都很长, 更可能是数据量整体过大或资源不足,应回到第三章查队列与第四章查 Map 数。除 TOP N 外,建议再看 空值桶:许多 Join 倾斜不是「头部太大」,而是「默认值太多」——加盐对 0 值往往不如直接过滤有效。 优化后对比四项:总耗时、最慢 task、preempt 次数、结果准确性;并确认 EXPLAIN 中 MapJoin 真正生效。

四步法里最容易偷懒的是第四步「验证回归」。只看「这次跑完了」不够:必须同 SQL、同数据量窗口对比 counter, 并确认 preempt 次数下降而不是碰巧没有 adhoc 冲进来。若加盐后最慢 task 缩短但总耗时反而上升, 可能是盐度过高导致 Reduce 数暴涨、调度开销吞噬收益——此时应回调盐桶数,而不是继续加大并行度。 框架训练的目标,是让值班同学在压力下仍能按步骤产出可复验证据,而不是凭感觉选一种「听说能治倾斜」的参数。

与 T02 的边界再强调一次:T02 讲清楚建表、分区、Explain 与各类倾斜改写的语法细节;本篇只保留值班必需的 识别判据与选型顺序。若你在现场已经确认是 GROUP BY 热点,具体两阶段写法以 T02 为准;若你还不确定是倾斜、 抢占还是 Shuffle 假死,应先走本篇第五至七章的分诊,再进入改写。

复合案例 · 倾斜叠加抢占

6案例一:大促回刷倾斜叠加 YARN 抢占

标注:复合场景,要素来自常见生产模式组合,非单一真实事故拷贝。

6.1 背景与现象

大促后需回刷 7 天订单汇总以修正退款口径,下游 ads_trade_report 依赖 dws_order_agg 在 06:00 前就绪。常规定时 45 分钟完成;回刷窗口 02:30~05:00。集群约 120 节点,batch 队列白天占用约 40%, 凌晨 ETL 并发 30+ 作业——没有无限重试窗口。02:35 开始,03:20 Reduce 进度 76% 后多次重启; 03:58 YARN 状态 KILLED,Diagnostics 含 preemption。告警渠道包括调度失败短信、队列使用率超 85%、 Tez 最慢 task 超 40 分钟阈值。

工具命令 / 入口本次产出
YARN 终态yarn application -statusQueue=root.batch.etl,03:58 KILLED,含 preemption
队列快照yarn queue -status root.batch.etlUsed 82%,Max 80%,已突破弹性上限
pending 枚举yarn application -list -appStates ACCEPTEDadhoc 12 个 ACCEPTED
Tez task 分布Tez UI → Reduce Vertex1/120 task 47min,其余 3~5min
倾斜采样shop_id GROUP BY Top10top1 shop 占 38% 行数
preempt 日志yarn logs | grep -i preempt03:45、03:52 Container preempted,03:58 Application killed

6.2 时间线与根因

时刻观察当时误判正确动作
02:35任务提交,Map 正常记录 applicationId
03:05Reduce 启动,1/120 task 极慢「集群负载高」采样 shop_id 分布
03:20进度 76%,adhoc pending 上升「再加 reducer」查 queue 是否触顶
03:45AM 第一次 restart「Tez bug」grep preempt 日志
03:58KILLED,Diagnostics preempt「纯资源问题」确认倾斜仍存在

主根因shop_id 严重倾斜导致单 Reduce 长尾。 放大因子:倾斜任务占用大量 Container 且运行过久,在 batch 触顶后成为抢占首选 victim; adhoc 与 batch 未做凌晨隔离。因果链:大促流量集中 → top shop_id 哈希桶亿级 → 单 Reduce 47 分钟 → Used 超过 Max → adhoc pending 触发监视器 → Container 回收 → AM restart 后 Application KILLED。 缺少任一环节都不会在 03:58 以 preempt 终态失败——这是典型的数据层根因 + 资源层放大复合故障。

复盘时特别容易犯「归因单一化」:平台组看到 preempt 就改队列;数据组看到 shop_id 倾斜就只改 SQL。 正确结论必须写清两句:第一句说明若不存在倾斜,作业会在抢占窗口前结束;第二句说明若存在倾斜但凌晨封网, 作业虽慢却未必被 KILL。两句都成立,才能解释为何「只改一边」在历史故障里反复失效。 工单模板建议固定填写:根因层次、放大器层次、验证指标(最慢 Reduce / Used-Max / preempt 计数)三项。

-- 两阶段聚合 + 加盐打散热点 shop_id(Hive 3.1.x)
WITH salted AS (
  SELECT shop_id, CAST(RAND() * 32 AS INT) AS salt, order_amt
  FROM ods_order
  WHERE dt BETWEEN '2026-06-25' AND '2026-07-01'
),
partial AS (
  SELECT shop_id, salt, SUM(order_amt) AS partial_sum
  FROM salted GROUP BY shop_id, salt
)
SELECT shop_id, SUM(partial_sum) AS total_amt
FROM partial GROUP BY shop_id;

资源层同步:batch.etl maximum-capacity 临时上调;adhoc 00:00~06:00 禁止新提交; preemption.max_wait_before_kill 由 15s 调到 45s。改完后回刷 52 分钟完成,最慢 Reduce 8 分钟, preempt 归零,抽样聚合误差小于 0.01%。预防:大促前热点 key 清单、凌晨封网、Grafana 三指标同屏 (最慢 Reduce、Used/Max、preempt 计数),季度做「倾斜 SQL + 人为 adhoc 洪峰」演练。

生产实践

倾斜与抢占同时出现时,先缩短长尾(SQL / 参数),再谈队列扩容。 同一 application 名每周 preempt 超过 2 次,应进入技术债 backlog,而不是依赖值班重跑。 组织层面建立「大促回刷」专项 checklist:SQL 评审、队列预留、adhoc 封网三条,避免每年重复踩坑。

阶段案例一:倾斜 + 抢占
现象Reduce 长尾 + Application preempt KILL
工具YARN Diagnostics、Tez task 耗时分布、倾斜 key 采样 SQL
定位确认倾斜 key 存在且失败前队列争用
方案两阶段聚合 / 加盐;时间窗隔离;适度调抢占等待与 max-capacity
预防大促前倾斜演练;batch/adhoc 分队列;preempt 事件告警
复合案例 · Join 倾斜卡顿

7案例二:维表 Join 倾斜引发 Shuffle 假死

标注:复合场景。与案例一不同:终态始终 RUNNING,无 KILL,易被误判为「集群算力不足需加机器」。

7.1 现象与工具链

任务 INSERT OVERWRITE ads_user_profile:事实表 dwd_event 单日约 8 亿行 Join 维表 dim_user 约 2000 万行(CSV 外表未分桶)。开发环境数据量小未暴露问题。 上线后 YARN 状态 RUNNING 超过 2 小时,Tez UI Shuffle 进度约 12% 停滞;无 preempt 记录; 部分 NM outbound 带宽打满。队列 Used 约 55%,可排除纯资源不足。

工具本次产出
EXPLAIN EXTENDEDCommon Join,MapJoin 未触发,维表约 1.8GB 超广播阈值
Tez DAG TimelineShuffle Vertex 占总时长 78%,Map 仅 12%
CounterSpill 为基线 18 倍;单 Map output 约 1.2TB
Join key 分布user_id=0 约 1.2 亿行(占当日 15%),第二名仅约 800 万
网络监控若干 NM outbound 持续 90%+,与活跃 Shuffle task 节点重合

7.2 根因、方案与预防

脏 key user_id=0 造成 Join 倾斜;维表未过滤无效用户;Shuffle 中间数据膨胀导致「假死」—— 进度条缓慢而非单 Reduce 99%。因果链:ODS 未校验 user_id → 15% 行落入 0 值 → Common Join 同 key 海量匹配 → 单 Map 输出 TB 级 → Shuffle 网络热点 → 进度 12% 停滞两小时。仅调 Tez 参数无法消除 1.2 亿行脏 key 带来的膨胀; 仅过滤而不检查 MapJoin 仍会留下 Common Join 开销。

与案例一对照:案例一终态是 KILLED,入口必须先走 YARN;案例二终态一直是 RUNNING,入口应先走 Tez 阶段与 EXPLAIN。两者都叫「倾斜」,但证据链不同——前者用 task 耗时分布打穿 Reduce 长尾,后者用 Spill 与 Join key TopN 打穿 Shuffle 膨胀。把两套证据链写进 Runbook,比再增加一张「倾斜参数大全」更有用。

-- 过滤脏 key + MapJoin(维表可广播时)
SET hive.auto.convert.join=true;
SET hive.auto.convert.join.noconditionaltask.size=512000000;

INSERT OVERWRITE TABLE ads_user_profile PARTITION (dt='2026-08-01')
SELECT /*+ MAPJOIN(dim) */
  f.event_id, f.user_id, dim.user_level, dim.register_channel
FROM (
  SELECT * FROM dwd_event
  WHERE dt='2026-08-01' AND user_id <> 0 AND user_id IS NOT NULL
) f
JOIN dim_user dim ON f.user_id = dim.user_id;

数据治理侧:ODS 增加 user_id 有效性规则;DWD 默认过滤 user_id=0。 优化后 Shuffle 数据量降约 62%,总耗时约 38 分钟。若维表无法广播,采用热点分离:将 user_id=0 事实行单独写入无效分区,主链路 Join 干净数据。 预防:Join Code Review 附 key 基数截图;维表体积接近广播阈值时需论证为何不 MapJoin; Shuffle 进度长时间低位告警;SPILLED_RECORDS 超基线数倍告警;维表行数突增触发阈值重评。

常见误区

Join 类任务卡 Shuffle,优先查脏 key / 空 key / 默认枚举值,再查网络。 维表接近广播阈值时应显式 MAPJOIN 或拆热点链路。分桶表仅对分布均匀的 key 有效—— 脏数据不治理,分桶也救不了倾斜。测试环境应用生产级抽样(如十分之一分区)做 Join 压测,避免「小数据上线必炸」。

阶段案例二:Join 倾斜 + Shuffle 卡顿
现象Shuffle 进度长期低位;Spill counter 异常;带宽打满
工具EXPLAIN EXTENDED、Tez counter、Join key 分布 SQL
定位脏 key 或热点 key 导致 Shuffle 数据倾斜
方案过滤脏 key;MapJoin / Skew Join;拆分热点链路
预防ODS/DWD 数据质量规则;Join 前 key 基数评估纳入 Code Review
适合 · 不适合

8什么时候该深挖,什么时候该先止血

SOP 手册容易让人「工具上瘾」:明明 SLA 已破、下游报表已延迟,仍坚持完整采样与加盐改写。 反过来说,也有人把每次 Reduce 毛刺都当成必须改 SQL 的倾斜。边界如下——适合与不适合不是价值判断, 而是对可观测窗口与业务损失的权衡。

适合深挖本篇 SOP

  • 任务仍 RUNNING 或可重跑同分区,证据窗口未关闭
  • Tez UI / History 可访问,能拿到 task 分布与 counter
  • 同类 SQL 近期重复失败,需要复盘与预防回流
  • 怀疑倾斜诱发抢占,需要时间线交叉验证
  • Join / 聚合上线前评审,主动做 key 基数评估

不适合「先分析后恢复」

  • 核心报表 SLA 已破且有明确回滚 / 降级路径——先止血再离线复盘
  • 整个 leaf 队列 ACCEPTED 堆积、平台组已启动紧急限流
  • NM 磁盘打满导致多任务 FAILED——先清磁盘再谈倾斜
  • Metastore 全局锁导致编译全停——先释放锁 / 限流 HS2
  • 无 Tez History、无日志权限——应走平台侧采集,而不是空转

也建议在架构评审里把「排障证据是否可执行」列为上线检查项:调度平台是否保留 SQL 版本与 applicationId、 Tez History 是否可达、队列与 preempt 是否进监控、大促窗口是否有 adhoc 封网策略。 缺一项,值班时就会被迫在压力下临时造轮子。工具链手册只有嵌进发布与值班流程,才从「好看的文档」变成「可执行的组织能力」。

一句话决策:SLA 未破且证据链完整时,值得深挖;SLA 已破或平台级故障已宣告时,先止血、后复盘。 深挖不是「更敬业」,止血也不是「不专业」——二者对应不同损失函数。把这条边界写进值班频道置顶, 可以减少「明明该降级却还在采样」与「明明该复盘却只重跑」两类组织浪费。

生产实践 · 统一 SOP

9统一排障 SOP 与大促预防清单

将第三至七章收敛为一线值班可打印的总表与决策树:先资源、后阶段、再数据。 使用时先匹配「故障现象」列,再横向展开五列动作。若约 15 分钟内无法归入任一行,升级到平台组二线, 附带 applicationId、Tez DAG 截图、queue -status CSV 三件套。

图 5 · 统一排障决策树(资源 → 阶段 → 数据)
自制示意图
Hive / YARN 任务异常 KILLED 且 Diagnostics 含 preempt? 是 → 第三章 / 第六章 队列 · 时间窗 · 交叉 Tez 否 → 看 Tez 阶段 Map / Shuffle / Reduce Map 慢 / Map 数过多 小文件 · 分区 · grouping Shuffle 慢 / 进度个位数 Spill · 网络 · Join(第七章) Reduce 单 Task 极慢 倾斜加盐 · 两阶段(第六) 均匀慢:集群资源满?是 → 回资源层;否 → Metastore 锁 / HS2 / HDFS 读慢 处置后必须同 SQL 同数据量对比 counter 基线,确认根因消失而非偶然 升级条件:连续 3 次 preempt;Shuffle 1 小时无变化且 Spill 持续升;NM 磁盘超 90%
文字等价:先问 preempt → 再问卡在哪个 Vertex → 最后才查 Metastore / 存储层。
来源:Apache Tez · Runtime Configuration(tez.apache.org);与 Hive / YARN 官方文档交叉对照后固化为本篇决策树。
故障现象 排查工具链 根因定位要点 处置方案 预防机制
Application KILLED,含 preemption yarn application -status;queue-status;capacity XML 失败时刻 batch/adhoc 使用率;max-capacity;高优 pending 时间窗隔离;调 max-capacity / 抢占等待;降 Container 碎片 队列月度评审;preempt 监控;批 adhoc 分队列
RUNNING 数小时无产出 Tez UI;counter;EXPLAIN EXTENDED 定位 Map / Shuffle / Reduce;最慢 task 分布 按阶段调 Tez/Hive;MapJoin;合并小文件 上线前 Explain 审查;SLA 分级
Reduce 99%,单 Task 超长 Tez task 视图;key 分布采样 热点 key / 长尾 GROUP BY / 脏枚举值 groupby.skewindata;加盐两阶段;拆分热点 SQL 大促热点清单;DWD 脏数据规则
Shuffle 进度极低,Spill 高 SPILLED_RECORDS;网络监控 Join 输出膨胀;脏 key 过滤脏 key;MapJoin;调 sort buffer Join Code Review;维表广播阈值规范
反复 AM restart,Container 骤降 RM 日志;grep preempt 与抢占叠加;或 NM 磁盘满 先治倾斜缩短任务;NM 磁盘清理 NM 磁盘水位告警;与倾斜治理联动
Map 数爆炸,单 Map 秒级结束 hdfs dfs -count;Tez Map Vertex 小文件过多;grouping 过小 合并小文件;调 grouping;分区 compact ODS 单文件体积规范;分区文件数监控
长期 ACCEPTED,AM 无法启动 application -list ACCEPTED;queue-status leaf 满;AM 资源占比触顶;user-limit 释放低优作业;临时调高 max-capacity AM 占比监控;提交前 queue 校验
编译阶段卡住,Tez 未启动 SHOW LOCKS;Metastore 慢查询;HS2 日志 元数据锁;CBO 超时;HS2 连接池耗尽 释放锁;ANALYZE TABLE;扩容或限流 HS2 锁超时告警;核心表统计定时更新
Reduce OOM / Java heap space task 日志;GC_TIME_MILLIS 倾斜导致单 task 过大;内存参数偏低 先治倾斜;再调 tez.task.resource.memory.mb 单 task input groups 基线;勿盲目加倍内存
复合:倾斜 + preempt KILL 六步命令链 + Tez Reduce 分布 + key 采样 倾斜拉长作业 → 队列触顶 → 被选为 victim 先 SQL 打散;再时间窗;最后调 max / wait 三指标同屏;大促前倾斜演练
复合:脏 key + Shuffle 假死 Explain + Spill + key TopN + NM 带宽 Join 输出倾斜 → Shuffle 网络热点 过滤脏 key;MapJoin / 热点分离 ODS 有效性规则;Shuffle 进度 SLA 告警

大促 / 回刷窗口专项预防清单

检查项标准责任人
batch 队列 max-capacity 预留回刷窗口内单独上调或独立 leaf 队列平台组
adhoc 凌晨封网00:00~06:00 无新 adhoc 提交或自动 kill平台组 / 调度
热点 key 清单大促店铺 / 类目已登记,SQL 已加盐或两阶段数据开发
preempt 告警接入监控,值班 Runbook 含第三章 SOPSRE
回刷结果校验与抽样聚合误差小于业务阈值数据开发 / QA

将上表粘贴到内部 Wiki 时,建议在「工具」列加跳转链接:RM UI、Tez History、调度平台作业详情。 「预防」列绑定责任人:平台组负责队列与 preempt 监控,数据组负责倾斜 key 清单与脏数据规则, 应用组负责 SQL Explain 审查。三角责任清晰后,复合故障才不至于在值班电话里变成「三方互相等待」。

统一 SOP 的使用纪律有三条。第一,十五分钟内必须落到表中某一行;落不下去就升级,而不是继续「再试一个参数」。 第二,任何临时调参(max-capacity、抢占等待、广播阈值)必须写回滚时间与责任人,避免大促窗口结束后配置漂移。 第三,复合行(倾斜 + preempt、脏 key + Shuffle)优先于单行——若现象同时命中两行,按复合行的处置顺序执行, 不要并行开两套互相打架的改动。这三条比记住表里每一个单元格更重要。

决策框架 · 收尾

10决策矩阵与相邻知识地图

10.1 决策矩阵

现场信号 继续当前 SOP 切换 / 并行另一 SOP 选型式处置(临时)
KILLED + Diagnostics 含 preempt 第三章六步闭环 Tez 最慢 Reduce 极长 → 并行第六章倾斜治理 adhoc 封网 / 临时上调 max-capacity
RUNNING,Reduce 单 Task 99% 第五章四步法 + 第六章手法 同时队列触顶 → 并行抢占链路 先杀无关 adhoc,再改 SQL 重跑
RUNNING,Shuffle 个位数,Spill 倍增 第七章 Join / 脏 key 路径 队列 Used 很高 → 排除纯资源误判后仍查数据层 限流并发 Join;降级跳过非关键维表字段
Map 数爆炸,均匀极慢 第四章 Map / 小文件路径 伴随 ACCEPTED 堆积 → 回资源层 调 grouping;合并输入;收紧分区
编译卡住,Tez 未启动 Metastore / HS2 路径 与任务倾斜无关,勿套用加盐 释放锁;限流 HS2;补统计信息
连续 3 次同 SQL preempt 停止「只重跑」 升级二线 + 进入倾斜技术债 窗口外重调度;临时独立队列

10.2 相邻知识地图

  • T01 Hadoop 核心架构:理解 YARN 队列与 Container 生命周期,才能读懂抢占 Diagnostics。
  • T02 Hive 深度实战:Explain、MapJoin、倾斜参数与建表规范的「怎么写对」侧。
  • T03 离线数仓分层:把脏 key 过滤与质量规则前移到 ODS / DWD,从排障走向预防。
  • T19 Kafka 高可靠:若故障表现为实时链路堆积而非离线 Tez,切换到消息积压 SOP。
阶段性结论

三类故障共享「现象 → 工具 → 定位 → 方案 → 预防」五列,但不共享入口。 值班价值不在于记住更多参数,而在于缩短「从并发告警到正确层次」的路径。 框架训练可迁移;现场数字必须用本集群复测。把决策矩阵贴在值班频道置顶,比再开一次参数培训更能减少无效重跑。

决策矩阵的读法是「先匹配现场信号,再选继续 / 切换 / 临时选型」三列,而不是从上到下背诵。 「继续当前 SOP」表示证据链仍闭合;「切换 / 并行」表示出现跨层信号,需要同时采集另一层证据; 「选型式处置」是止血动作,事后必须回到对应章节做根因闭环。若团队只执行第三列而不回写第一、二列, 会把临时手段固化成长期配置债。

10.3 带走什么

  • 排障顺序:先分资源 / 执行 / 数据 → 再选工具 → 时间线对齐 → 处置 → 预防
  • 抢占常是症状在资源层、病因可能在数据层;单纯加并行度可能更易成为 victim。
  • Hive Tez 卡顿必须定位到 Map、Shuffle、Reduce;「99%」与「Shuffle 12%」指向不同方案。
  • 倾斜识别四步法(确认 → 定位 key → 选手法 → 验证)可脱离具体案例复用。
  • 两个复合案例共性:热点 / 脏 key 是数据层元凶;队列与时间窗是放大器;处置顺序是缩短长尾 → 隔离资源 → 调参。
  • 本篇结论基于 Hadoop 3.3.x + Hive 3.1.x + Tez;MR 引擎阶段名不同但分层思路仍适用。

本篇以复合场景串联抢占、卡顿与倾斜,核心交付是三层排障地图 + 双案例全链路 + 统一 SOP + 决策矩阵。 下一步请把 SOP 表与决策树写进团队 Runbook,并在大促前完成一次「倾斜 SQL + adhoc 洪峰」演练—— 那一页纸与那次演练,才是这篇分享真正留下的东西。若只有三十分钟阅读时间,请优先带走:先读 YARN Diagnostics、 再看 Tez 阶段、倾斜四步法,以及「先缩短长尾再调队列」的处置顺序。

最后提醒版本边界:文中命令与参数名以 Hadoop 3.3.x、Hive 3.1.x、Tez 为准;若集群仍跑 Fair Scheduler 或 MapReduce 引擎,抢占触发条件与 UI 入口会不同,但「先资源、后阶段、再数据」的分层不变量仍然成立。 迁移到 Spark SQL 时,也可把同一决策树映射到 Stage / Shuffle Read 视图,只是工具名需要替换。