背景:MapReduce Reduce 任务,某个 key 的记录量爆炸,单个 Reducer 扛大量数据,出现数据倾斜,就是热点 Key。
核心:ReduceTask 日志 → 找到倾斜的具体 key 值、条数。
一、先明确:什么是热点 Key
MR 流程:Map 输出 <key,value>,按 key 哈希分区,同一个 key 全部发给同一个 Reducer。
热点 Key = 某一个 key 对应的记录数远大于其他 key,数据全部打到同一个 reduce,这个 reduce 任务慢、OOM、长时间卡死,其他 reduce 很快跑完。
举例子:
- 正常:每个 key 几千条
- 热点 key:
key=unknown直接几十万 / 几百万条,全部进同一个 reduce。
二、第一步:下载 ReduceTask 日志
1. 找到任务的 YARN 页面
- 访问 ResourceManager UI
- 找到你的 Application ID:
application_xxxxxx,点进去 - 切换到 Logs → 找到对应 Reduce Task
- TaskType:
REDUCE - TaskAttemptId:
attempt_xxxxxx(选失败 / 卡住的那个 reduce 实例)
- TaskType:
- 下载日志:一般分 3 个文件
stdout:业务代码打印日志(重点!我们要搜的 key 打印在这里)stderr:框架异常堆栈(OOM、GC 日志)syslog:YARN 容器系统日志
命令行下载(集群服务器上)
# yarn logs 直接拉这个reduce attempt的全部日志到本地文件
yarn logs -applicationId application_1234567890123_0001 -containerId container_xxxxxx > reduce_task.log
三、前提:代码里必须有 key 打印(非常关键)
如果你没有在 reduce 函数打印 key 和计数,日志里根本搜不到 key!
Reduce 伪代码示例(需要埋点)
java 运行
@Override
protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context){
long count = 0;
for(Object val : values){
count++;
}
// 埋点打印:输出key + 当前key的记录条数,日志里才能检索
System.out.println("REDUCE_KEY_COUNT,key=" + key.toString() + ",count=" + count);
context.write(key,count);
}
打印样例日志行:
REDUCE_KEY_COUNT,key=999999,count=386291
REDUCE_KEY_COUNT,key=123456,count=1200
REDUCE_KEY_COUNT,key=888888,count=956
日志关键字标记:
REDUCE_KEY_COUNT(自定义标记,方便 grep 过滤)
四、【核心】怎么搜索、定位热点 Key
日志文件:reduce_task.log
1. 过滤所有 key 计数日志行
# 提取所有包含标记的行
grep "REDUCE_KEY_COUNT" reduce_task.log > key_stat.txt
2. 提取 key 和数字,按条数排序,找到爆炸的 key
# 提取count数值,倒序,数量最大的在最前面
awk -F ',' '{print $2}' key_stat.txt | sort -t '=' -k2 -n -r
-F ','按逗号分割sort -n -r数字、倒序排序,数字最大的就是热点 key
示例输出:
key=unknown,count=4628912
key=000001,count=1256
key=000002,count=987
👉 一眼看到:key=unknown 就是热点 key!
五、如果没有埋点打印 key(很多线上代码没加这个)
没法直接拿到 key 值,只能间接判断:
- 看 Reduce 任务的计数器(YARN UI Counters)
MapReduce Framework -> Reduce input groups:key 分组数量;Reduce input records:这个 reduce 收到总记录。
如果一个 reduce:输入记录几百万,但是 group 只有个位数 → 大概率存在热点 key。
- 方案:临时修改 reduce 代码,增加 key 计数打印,重跑任务抓取日志。
六、怎么判断这个 key 是不是热点?判断标准
- 对比其他 Reducer 其他 reduce 任务:每个 reduce 输入记录几千~几万; 这个倾斜 reduce:几百万甚至上千万记录。
- 单 key 的记录数,远超平均值 计算平均记录数 = 总 reduce 输入记录 /key 分组数量
某个 key 条数 >> 平均值几十倍上百倍 → 热点 key。
七、拿到热点 key 之后怎么处理(数据倾斜解决方案)
-
过滤:如果是脏 key(
unknown、空字符串、null、-999),map 阶段直接过滤掉 -
加盐打散:热点 key 后面加随机后缀,拆分到多个 reducer,reduce 端二次聚合
原key: unknown
加盐后: unknown_0, unknown_1 ... unknown_9 分到不同reduce -
拆分任务、调整 reducer 数量,治标不治本;优先处理倾斜 key。
八、常见踩坑
- ❌ 搜 syslog/stderr:业务打印的 key 一般不在里面,要看 stdout
- ❌ 日志太多,直接 vi 打开卡死:一定要 grep 先过滤输出小文件
- ❌ 没有埋点打印 key:日志里没有 key 字段,只能看到记录数量,拿不到具体 key 的值
- ❌ 区分:
Reduce input records是总条数,Reduce input groups是 key 的个数;group 很小,records 巨大 = 典型倾斜