从零数清楚:MapReduce 里 Map 究竟干了啥,Reduce 又干了啥?——以及“到底什么时候可以省掉 Reduce”

  • Home
  • 攻略研究院
  • 从零数清楚:MapReduce 里 Map 究竟干了啥,Reduce 又干了啥?——以及“到底什么时候可以省掉 Reduce”

一、先把角色放到舞台上

阶段输入输出核心职责生活比喻Map一行行原始记录 对把“看不懂”的原始数据,变成“能归类”的 KV 数据把混合垃圾里的塑料瓶、纸箱分别扔进不同桶ReduceMap 吐出的 最终 结果对同一类 key 做汇总/连接/二次加工把同一桶里的塑料瓶压扁打包,算总重量

二、举个例子:

输入文件内容:

hello world

hello java

Map 输出(中间结果):

(hello, 1)

(world, 1)

(hello, 1)

(java, 1)

Reduce 输入(按 key 分组):

hello: [1, 1]

world: [1]

java: [1]

Reduce 输出(最终结果):

hello 2

world 1

java 1

三、Reduce 的“高光时刻”——必须上 Reduce 的场景

业务特征例子原因需要全局聚合PV、UV、销售额总计同一 key 散落在不同节点,必须二次汇总需要排序输出每天 Top10 热搜Reduce 阶段自带按 key 排序(可配)需要多源连接订单表 join 用户表Map 阶段只能局部 join,全局需 Reduce需要去重计数独立访客、唯一设备同一 ID 可能散在不同 map,需全局去重

一句话:只要 key 的“全集”需要被重新洗牌到同一节点,就离不开 Reduce。

四、可以省掉 Reduce 的场合——Only-Map 任务

把 job.setNumReduceTasks(0)(或 job.setReducerClass(Reducer.class) 干脆不写),框架会跳过 Shuffle & Reduce,Map 直接写 HDFS。

适用场景:

纯过滤 / 转换

例:日志脱敏、字段截取、格式转换——每条记录独立处理,不依赖其他记录。

局部聚合就能满足

例:Combiner 已把 合并成 ,业务只关心文件级结果,不要求全局排序。

下游自己再聚合

例:Map 先打成 1000 个小文件,下游 Spark 或 ClickHouse 再并行汇总,把二次聚合职责推给下游引擎。

数据量极小,Shuffle 反而亏本

例:配置文件解析,输出几千行,Reduce 启动开销比计算还大。

代码模板:

job.setMapperClass(MyMapper.class);

job.setNumReduceTasks(0); // 关键一句

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(NullWritable.class);

五、Combiner——“本地小 Reduce”不能代替全局 Reduce

很多人把 Combiner 当成“省 Reduce”的银弹,其实它只能减少网络 IO,无法替代跨节点的全局聚合。

Combiner 运行在 Map 端 JVM,输出仍是 ,框架会把多个 Map 的同一 key 再次 Shuffle 到 Reduce。

所以:

计算可结合 & 可交换(sum、max、min)→ 加 Combiner 提速

求中位数、去重计数(精确)→ Combiner 无效,必须走 Reduce

六、面试快问快答

Q1:WordCount 可以把 Reduce 省掉吗?

答:如果只想看“每个 map 文件里的词频”,可以;但通常要全局词频,就必须 Reduce。

Q2:设置 0 个 Reduce 后,输出文件长啥样?

答:Map 直接输出到 HDFS,文件名保持 part-m-00000,不再出现 part-r-xxx。

Q3:0 Reduce 还能用 Combiner 吗?

答:不能!Combiner 是 Map → Reduce 之间的优化,没有 Reduce 阶段也就不会调用 Combiner。

七、一句话收个尾

Map 负责“分类”,Reduce 负责“汇总”;

当业务只需要“分类”而无需“汇总”时,放心把 Reduce 踢掉,让数据从 Map 端直飞磁盘,省时又省带宽。