MapReduce(期末速成版)

起初在B站看3分钟的速成视频,感觉很多细节没听懂。


具体例子解析(文件内容去重)

对于两个输入文件,即文件A 和文件B,请编写MapReduce 程序,对两个文件进行合并,并剔除
其中重复的内容,得到一个新的输出文件C。

📂 一、输入数据文件

文件 A:

20150101 x
20150102 y
20150103 x
20150104 y
20150105 z
20150106 x

文件 B:

20150101 y
20150102 y
20150103 x
20150104 z
20150105 y

🧠 二、MapReduce 执行流程和中间结果

MapReduce 分为三个主要阶段:

  1. Map 阶段

  2. Shuffle(分组 & 排序)阶段

  3. Reduce 阶段


🔹2.1 Map 阶段(映射阶段)

我们先来看下 Mapper 的代码逻辑:

public static class Map extends Mapper<Object, Text, Text, Text> {private static Text text = new Text();public void map(Object key, Text value, Context context) {text = value;context.write(text, new Text(""));}
}
🔍 Mapper 做了什么?
  • 每行文本被视为一个输入记录(value),key 是字节偏移量(无关紧要)。

  • Mapper 不对数据做任何处理,直接原样输出 value 作为 key,并给定空字符串作为 value

  • 这样,相同行的数据(A、B 中相同的行)会生成相同的 key,从而可以在 Shuffle 阶段合并。


🔢 Map 输出结果(中间键值对)

我们对 A、B 两个文件的所有行执行一次 map() 操作,得到如下中间结果(<key, value> 形式):

来源key(Text)value(Text)
A20150101 x""
A20150102 y""
A20150103 x""
A20150104 y""
A20150105 z""
A20150106 x""
B20150101 y""
B20150102 y""
B20150103 x""
B20150104 z""
B20150105 y""

🔹2.2 Shuffle 阶段(分组 & 排序)

MapReduce 框架自动完成以下操作:

  • 将所有 Mapper 输出结果根据 key 进行哈希分区、排序、去重分组

  • 每一个唯一的 key 会被送入一次 Reducer。

🎯 分组结果(Reducer 接收到的 key 和 values):
key(唯一行)values("" 的列表)
20150101 x["",]
20150101 y["",]
20150102 y["", ""]
20150103 x["", ""]
20150104 y["",]
20150104 z["",]
20150105 y["",]
20150105 z["",]
20150106 x["",]

⚠️ 注意:

  • 20150102 y20150103 x 都在两个文件中出现了,所以它们的 values 有两个空字符串。

  • 但 Reducer 并不关心这些 values,它只输出唯一的 key


🔹2.3 Reduce 阶段(归约阶段)

看一下 Reducer 的代码:

public static class Reduce extends Reducer<Text, Text, Text, Text> {public void reduce(Text key, Iterable<Text> values, Context context) {context.write(key, new Text(""));}
}
🔍 Reducer 做了什么?
  • 对于每一个唯一的 key,Reducer 被调用一次。

  • 忽略 values,直接输出 key 和空的 Text("")

  • 实际效果是:只输出不重复的唯一行内容


✅ 最终输出文件 C 的内容:

20150101 x
20150101 y
20150102 y
20150103 x
20150104 y
20150104 z
20150105 y
20150105 z
20150106 x

✅ 总结

步骤说明
Map对输入的每一行输出 <Text(该行), Text("")>
Shuffle根据行内容去重、分组、排序
Reduce忽略 values,只输出唯一的 key(行内容)
输出文件文件 A 和 B 合并去重后的内容

问题一:Reduce端是如何输出文件的?

✅ Reduce中context.write(key, value) 的行为

在 Hadoop MapReduce 中:

context.write(new Text("s"), new Text("a"));

的输出行为是:

  • 每一行输出格式为:

    key \t value
    

    即,key 和 value 之间用一个制表符(Tab 字符 \t)分隔


🔍 所以你举的例子

context.write(new Text("s"), new Text("a"));

最终输出文件中的一行会是:

s	a

不是 sa,而是 sa 之间有一个 Tab 分隔符。


🔧 那么在你的代码中:

context.write(key, new Text(""));

由于 value 是空字符串,所以每一行就是:

key

即没有显示的 value,只输出 key 的内容,所以:

20150101 x

这行实际上是 key 的原样内容,不是 key + "" 的拼接结果,而是 key 后面虽然有个空字符串作为 value,但由于 value 是空的,输出就是 key 自身。


✍️ 补充:可以自定义 key 和 value 的分隔符

在 Hadoop 中默认的 key/value 分隔符是 Tab(\t),但你可以通过设置:

-D mapreduce.output.textoutputformat.separator=","

来更改分隔符,比如改成逗号,则输出会变成:

s,a


✅ 是不是有几个 Reducer 就会产生几个输出文件?

是的,完全正确。

在 Hadoop MapReduce 中:

  • 如果你设置了 N 个 Reducer 任务(比如 job.setNumReduceTasks(N)),

  • 那么就会产生 N 个输出文件

这些输出文件的名称通常是:

part-r-00000
part-r-00001
...
part-r-00(N-1)

每个文件由一个 Reducer 任务写出。


❓ 那这些输出文件的内容一致吗?

不一致!每个文件的内容不同!

➤ 原因:

  • MapReduce 框架会按照 key 的 hash 值把数据**分区(Partition)**给不同的 Reducer。

  • 每个 Reducer 只处理自己分到的 key 分区。

  • 所以:

    • 每个输出文件包含的是不同部分的 key-value 对

    • 输出文件之间是不重合的,也就是说每个 key 只会出现在一个 Reducer 的输出文件中


🧠 举个例子(比如有 2 个 Reducer):

假设你有以下中间 key:

20150101 x
20150101 y
20150102 y
20150103 x
20150104 y
20150104 z
20150105 y
20150105 z
20150106 x
Hadoop 会通过 partitioner(比如默认的 HashPartitioner)决定:
  • Reducer 0 处理:

    20150101 x
    20150103 x
    20150105 z
    20150106 x
    
  • Reducer 1 处理:

    20150101 y
    20150102 y
    20150104 y
    20150104 z
    20150105 y
    

然后产生:

  • part-r-00000 ← Reducer 0 写入

  • part-r-00001 ← Reducer 1 写入

两个文件中的内容互不重复,但合起来是完整的结果。



✅ 1. 默认是不是只有一个 Reducer?

是的,默认情况下 Hadoop MapReduce 只使用 1 个 Reducer。

也就是说,如果你没有显式调用:

job.setNumReduceTasks(N);

则默认 N = 1,最终只会生成一个输出文件:part-r-00000


✅ 2. 可以设置多个 Reducer 吗?

当然可以,而且非常常见。

你可以在驱动代码中显式设置 Reducer 个数,例如设置为 3:

job.setNumReduceTasks(3);

这样 Hadoop 会启用 3 个 Reducer 并行处理数据,输出三个文件:

part-r-00000
part-r-00001
part-r-00002

问题二:Shuffle过程的输出结果与Combiner函数本质是?


✅ 一、Shuffle 输出是啥?

默认情况下:
Map 阶段的输出会经过 Shuffle(排序 + 分区 + 组装) 后变成:

key1 → [v1, v2, v3, ...]
key2 → [v1, v2, ...]
...

这些最终被送入 Reducer。


❓ 问题:为什么会有重复的 value?

因为 同一个 key 可能在同一个 Mapper 中出现多次,比如:

hello → 1
hello → 1
hello → 1

这些数据在传输前就可以局部聚合,先加一加再传过去,不用浪费网络带宽


✅ 二、Combiner 是什么?

Combiner 就是一个 “局部 Reduce”,在 Mapper 端执行,用来提前聚合。

它的作用是:

  • 在 Mapper 本地 就先对 key 进行累加(或合并),

  • 减少大量重复的 <key, 1> 传给 Reducer,

  • 降低网络传输压力,提升性能。


✅ 三、怎么写一个 Combiner?

👉 很简单,其实你可以直接 复用 Reducer 逻辑,只要满足:聚合操作是可交换和结合的(比如加法)

✅ 1. 定义 Combiner 类(和 Reducer 一样):

public static class IntSumCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {private IntWritable result = new IntWritable();public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {int sum = 0;for (IntWritable val : values) {sum += val.get();}result.set(sum);context.write(key, result);  // 输出 key sum}
}

✅ 2. 在 Driver 中设置:

job.setCombinerClass(IntSumCombiner.class);

⚠️ 注意:你也可以直接写成:

job.setCombinerClass(IntSumReducer.class);

因为本质一样(统计加法是符合条件的操作)。


✅ 四、添加了 Combiner 后的数据流是什么样?

假设有两个 Map 输出如下:

Mapper1 输出:

hello → 1
hello → 1
world → 1

经过 Combiner:

hello → 2
world → 1

Mapper2 输出:

hello → 1
world → 1

经过 Combiner:

hello → 1
world → 1

最终 Shuffle 输出给 Reducer:

hello → [2, 1]
world → [1, 1]

Reducer 再聚合:

hello → 3
world → 2

✅ 五、什么时候不要用 Combiner?

虽然 Combiner 很有用,但它不是 always-safe 的,只有在满足可交换、可结合的前提下才可用。

操作类型适合使用 Combiner?示例
加法、计数、最大最小值✅ 可以用WordCount、MaxTemperature
求平均、TopN、排序❌ 不建议平均值不能分区计算后再平均

✅ 所以完整流程是:

Map → Combiner → Shuffle(聚合 + 分区)→ Reduce

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/82532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java高级 | 【实验四】Springboot 获取前端数据与返回Json数据

隶属文章&#xff1a; Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a; Java高级 | 【实验一】Spring Boot安装及测试 最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot …

从零打造AI面试系统全栈开发

&#x1f916; AI面试系统开发完整教程 &#x1f4cb; 项目概述 本教程将带你从零开始构建一个完整的AI面试系统&#xff0c;包含前端、后端、AI集成和部署的全流程。 源码地址 技术栈 前端: React TypeScript Vite Vaadin Components后端: Spring Boot Spring Securi…

【硬件】PCIe协议 | 电脑的高速公路

文章目录 PCIe | 外围设备高速互联通道&#xff08;peripheral component interconnect express&#xff09;的核心概念和应用 基础概念 1.1 电脑内的”高速“&#xff0c;连接CPU、显卡、SSD&#xff08;固态硬盘&#xff09;等核心组件&#xff1b;数据传输速度极快&#xff…

【 Redis | 完结篇 缓存优化 】

前言&#xff1a;本节包含常见redis缓存问题&#xff0c;包含缓存一致性问题&#xff0c;缓存雪崩&#xff0c;缓存穿透&#xff0c;缓存击穿问题及其解决方案 1. 缓存一致性 我们先看下目前企业用的最多的缓存模型。缓存的通用模型有三种&#xff1a; 缓存模型解释Cache Asi…

MySQL访问控制与账号管理:原理、技术与最佳实践

MySQL的安全体系建立在精细的访问控制和账号管理机制上。本文基于MySQL 9.3官方文档,深入解析其核心原理、关键技术、实用技巧和行业最佳实践。 一、访问控制核心原理:双重验证机制 连接验证 (Connection Verification) 客户端发起连接时,MySQL依据user_name@host_name组合进…

Go语言爬虫系列教程4:使用正则表达式解析HTML内容

Go语言爬虫系列教程4&#xff1a;使用正则表达式解析HTML内容 正则表达式&#xff08;Regular Expression&#xff0c;简称RegEx&#xff09;是处理文本数据的利器。在网络爬虫中&#xff0c;我们经常需要从HTML页面中提取特定的信息&#xff0c;正则表达式就像一个智能的&quo…

笔记 | docker构建失败

笔记 | docker构建失败 构建报错LOG1 rootThinkPad-FLY:/mnt/e/02-docker/ubunutu-vm# docker build -t ubuntu16.04:v1 . [] Building 714.5s (6/11) docker:default> [internal] load …

CentOS 7.9 安装 宝塔面板

在 CentOS 7.9 上安装 宝塔面板&#xff08;BT Panel&#xff09; 的完整步骤如下&#xff1a; 1. 准备工作 系统要求&#xff1a; CentOS 7.x&#xff08;推荐 7.9&#xff09;内存 ≥ 1GB&#xff08;建议 2GB&#xff09;硬盘 ≥ 20GBroot 权限&#xff08;需使用 root 用户…

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…

【AI News | 20250604】每日AI进展

AI Repos 1、jaaz Jaaz是一款免费开源的AI设计代理&#xff0c;作为Lovart的本地替代品&#xff0c;它能实现图像、海报、故事板的设计、编辑和生成。Jaaz集成了LLM&#xff0c;可智能生成提示并批量生成图像&#xff0c;支持Ollama、Stable Diffusion等本地及API模型。用户可…

Docker load 后镜像名称为空问题的解决方案

在使用 docker load命令从存档文件中加载Docker镜像时&#xff0c;有时会遇到镜像名称为空的情况。这种情况通常是由于在保存镜像时未正确标记镜像名称和标签&#xff0c;或者在加载镜像时出现了意外情况。本文将介绍如何诊断和解决这一问题。 一、问题描述 当使用 docker lo…

SQL进阶之旅 Day 14:数据透视与行列转换技巧

【SQL进阶之旅 Day 14】数据透视与行列转换技巧 开篇 欢迎来到“SQL进阶之旅”系列的第14天&#xff01;今天我们将探讨数据透视与行列转换技巧&#xff0c;这是数据分析和报表生成中的核心技能。无论你是数据库开发工程师、数据分析师还是后端开发人员&#xff0c;行转列或列…

haribote原型系统改进方向

在时钟中断、计时器和键盘输入方面&#xff0c;一些创新性的改进方向&#xff1a; 时钟中断 (PIT / inthandler20) 动态节拍 (Tickless Kernel)&#xff1a;当前的 PIT 中断以固定频率&#xff08;约 100Hz&#xff09;触发&#xff0c;即使系统空闲或没有即将到期的计时器&…

LabVIEW基于 DataSocket从 OPC 服务器读取数据

LabVIEW 中基于 DataSocket 函数从 OPC 服务器读取数据的功能&#xff0c;为工业自动化等场景下的数据交互提供了解决方案。通过特定函数实现 URL 指定、连接建立与管理、数据读取&#xff0c;相比传统 Socket 通信和 RESTful API &#xff0c;在 OPC 服务器数据交互场景有适配…

SimpleDateFormat 和 DateTimeFormatter 的异同

在Java开发中Date类型转String类型是比较常见的&#xff0c;其中最常用的是以下几种方式&#xff1a; 1. 使用SimpleDateFormat&#xff08;Java 8之前&#xff09; import java.text.SimpleDateFormat; import java.util.Date;public class DateToStringExample {public sta…

《前端面试题:CSS对浏览器兼容性》

CSS浏览器兼容性完全指南&#xff1a;从原理到实战 跨浏览器兼容性是前端开发的核心挑战&#xff0c;也是面试中的高频考点。查看所有css属性对各个浏览器兼容网站&#xff1a;https://caniuse.com 一、浏览器兼容性为何如此重要&#xff1f; 在当今多浏览器生态中&#xff0c…

【stm32开发板】单片机最小系统原理图设计

一、批量添加网络标签 可以选择浮动工具中的N&#xff0c;单独为引脚添加网络标签。 当芯片引脚非常多的时候&#xff0c;选中芯片&#xff0c;右键选择扇出网络标签/非连接标识 按住ctrl键即可选中多个引脚 点击将引脚名称填入网络名 就完成了引脚标签的批量添加 二、电源引…

golang连接sm3认证加密(app)

文章目录 环境文档用途详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 文档用途 golang连接安全版sm3认证加密数据库,驱动程序详见附件。 详细信息 1.下载Linux golang安装包 go1.17.3.linux-amd64.tar.gz 1.1. 解压安…

node实例应用

打开vscode,创建node项目,直接进入一个干净的文件夹&#xff0c;打开控制台 一 项目初始化 1. 初始化包管理 npm init -y2. 安装express npm install express4.17.1 3. 根目录下创建app.js,引入express // 引入expree const express require(express)// 创建实例 const …

Springboot——整合websocket并根据type区别处理

文章目录 前言架构思想项目结构代码实现依赖引入自定义注解定义具体的处理类定义 TypeAWebSocketHandler定义 TypeBWebSocketHandler 定义路由处理类配置类&#xff0c;绑定point制定前端页面编写测试接口方便跳转进入前端页面 测试验证结语 前言 之前写过一篇类似的博客&…