MapReduce 是一种分布式计算框架,核心目的是解决海量数据在普通服务器集群上的高效处理问题,它把复杂的分布式计算拆成两个核心阶段:Map(映射) 和 Reduce(归约),让不懂分布式的开发者也能轻松处理大数据。
你可以把它想象成一个 “工厂流水线”:一堆原材料(海量数据),经过两道核心工序后,变成最终产品(计算结果)。
一、核心思想:分而治之
面对海量数据,单台服务器处理会很慢甚至撑不住。MapReduce 的思路是:
- 拆分任务:把大数据切成很多小数据块,分给集群里的多台机器并行处理(Map 阶段)。
- 合并结果:把所有机器处理后的小结果,再汇总成最终的完整结果(Reduce 阶段)。
二、MapReduce 完整工作流程(5 个核心步骤)
我们用一个经典例子理解:统计一个超大文本里每个单词出现的次数(WordCount)。
1. 数据输入(Input)
- 数据源:比如一个 100GB 的文本文件,存在分布式文件系统(比如 HDFS)里。
- 分片(Split):框架自动把文件切成数据分片(比如每个分片 128MB),每个分片对应一个 Map 任务。
- 注意:分片只是逻辑划分,不是物理切割文件,避免浪费存储空间。
2. Map 阶段(映射:核心是 “拆分 + 初步处理”)
这是并行处理的核心,集群里的多台机器同时执行 Map 任务,每台机器处理一个数据分片。
- 输入:键值对(Key:Value),默认是
(行偏移量,该行文本内容)。比如分片里的一行文本是hello world,输入就是(0, "hello world")。 - 处理逻辑:开发者写的 Map 函数,做简单的过滤、拆分、转换。对上面的例子,Map 函数会把句子拆成单词,输出
(单词, 1)的键值对:(hello,1)、(world,1)。 - 输出:一堆
(单词,1)的临时键值对,存在本地磁盘(不是分布式存储)。
3. Shuffle 阶段(洗牌:框架自动完成,最关键的隐藏步骤)
Shuffle 是 Map 和 Reduce 之间的桥梁,作用是把 Map 的输出整理成 Reduce 能处理的格式,这个过程对开发者透明(不用写代码)。它包含 3 个关键操作:
- 分区(Partition):把 Map 输出的键值对,按 Key 分到不同的 Reduce 任务里。比如所有
hello的键值对都分给 Reduce1,所有world的分给 Reduce2(默认按 Key 的哈希值分区)。 - 排序(Sort):每个分区内的键值对,会按 Key 自动排序。比如 Reduce1 收到的是
(hello,1)、(hello,1)、(hello,1)... - 合并(Combine):对排序后的相同 Key 的值做局部汇总,减少传输数据量。比如把 3 个
(hello,1)合并成(hello,3),再传给 Reduce。
4. Reduce 阶段(归约:核心是 “汇总计算”)
Reduce 任务也是并行执行的,每个 Reduce 处理一个分区的数据。
- 输入:Shuffle 处理后的键值对,比如
(hello, [1,1,1])。 - 处理逻辑:开发者写的 Reduce 函数,做汇总、统计、聚合。对
hello的值列表求和,得到(hello,3)。 - 输出:最终结果,比如
hello 3,写入分布式文件系统(HDFS)。
5. 结果输出(Output)
把所有 Reduce 的输出汇总,就是最终的单词统计结果,存储在 HDFS 上,用户可以直接查看。
三、MapReduce 核心特点
- 并行计算:Map 和 Reduce 任务都能在集群上并行执行,大幅提升速度。
- 容错性强:如果某台机器宕机,框架会自动把任务转移到其他机器重新执行,不用人工干预。
- 简单易用:开发者只需要写 Map 和 Reduce 两个函数的逻辑,不用关心分布式通信、数据传输、容错这些复杂问题。
- 适合批处理:MapReduce 擅长离线的海量数据批处理(比如统计一天的日志),不适合实时计算(比如秒杀活动的实时销量统计)。
四、通俗类比:统计全校学生的身高平均值
- Map 阶段:把全校学生按班级分成多个小组(分片),每个班级(Map 任务)统计自己班每个人的身高,输出
(班级, 身高)。 - Shuffle 阶段:把所有班级的身高数据,按 “性别” 分区(男生一组,女生一组),并排序。
- Reduce 阶段:男生组(Reduce1)计算男生平均身高,女生组(Reduce2)计算女生平均身高,最终得到全校男女生的身高平均值。
五、Shuffle 阶段
Shuffle 是 MapReduce 中最核心、最复杂的隐藏阶段,它介于 Map 和 Reduce 之间,作用是将 Map 输出的零散数据,整理成 Reduce 能够高效处理的结构化数据。这个过程对开发者完全透明(无需编写代码),但直接决定了整个 MapReduce 任务的性能。
我们还是以 WordCount(单词统计) 为例,拆解 Shuffle 阶段的6 个关键步骤:
一、 Shuffle 的核心目标
- 数据分区:把 Map 输出的键值对(
单词,1),按照 Key(单词)分配给对应的 Reduce 任务,相同 Key 的数据必须到同一个 Reduce。 - 数据排序:每个 Reduce 收到的数据,必须按 Key 排序,方便后续聚合计算。
- 数据压缩:减少数据在集群内的传输量,提升效率。
二、 Shuffle 完整流程(从 Map 输出到 Reduce 输入)
1. Map 端:本地写缓存(Map Output Buffer)
- Map 函数输出的键值对,不会直接写入磁盘,而是先写入内存缓存区(默认大小 100MB),这是为了减少磁盘 IO 次数。
- 缓存区里会同时做两个操作:
- 分区(Partition):通过
Partitioner类计算每个键值对应该属于哪个 Reduce。默认规则是Hash(Key) % Reduce数量,比如 Reduce 数量是 2,hello的哈希值模 2 得 0,就分给 Reduce 0;world模 2 得 1,就分给 Reduce 1。 - 排序(Sort):每个分区内的键值对,会按 Key 的字典序实时排序。
- 分区(Partition):通过
2. Map 端:溢写(Spill)—— 内存到磁盘
当缓存区的使用率达到阈值(默认 80%) 时,会触发溢写操作:
- 把缓存区里的数据,按分区切割成多个临时文件,写入 Map 任务所在机器的本地磁盘(不是分布式存储 HDFS)。
- 溢写前会做一次 Combiner(局部合并):对同一个分区内的相同 Key 做聚合,比如把
(hello,1), (hello,1)合并成(hello,2),大幅减少后续传输的数据量。- 注意:Combiner 是可选的,只有当计算逻辑满足结合律和交换律时才能用(比如求和、计数可以用,求平均值不能用)。
3. Map 端:合并溢写文件(Merge)
Map 任务执行完后,本地磁盘会生成多个溢写临时文件。框架会把这些同分区的临时文件合并成一个大文件,并再次按 Key 排序,最终 Map 端输出的是:多个分区的、已排序的、合并后的文件。
4. Reduce 端:数据拷贝(Fetch)
Reduce 任务启动后,会主动向所有 Map 任务发起 HTTP 请求,拉取属于自己分区的数据。
- 比如 Reduce 0 会拉取所有 Map 任务中分区 0 的数据。
- 如果数据量太大,会开启并发拷贝,提升拉取速度。
5. Reduce 端:合并排序(Merge + Sort)
Reduce 拉取到来自不同 Map 的数据后,会把这些零散数据合并成一个大的有序文件:
- 这个合并是归并排序,因为每个 Map 传来的数据本身已经是有序的,归并排序效率极高。
- 合并过程中,如果内存放不下,会和 Map 端一样,触发磁盘溢写,最后再合并成一个最终的有序文件。
6. Reduce 端:数据输入 Reduce 函数
经过合并排序后,数据会以 (Key, 迭代器<Value>) 的形式输入 Reduce 函数。比如 (hello, [1,1,2]),Reduce 只需要对 Value 列表求和,就能得到最终结果。
三、 通俗类比 Shuffle 过程
你可以把 Shuffle 想象成 “班级统计身高” 中的 “数据汇总环节”:
- 每个小组(Map)统计完组员身高后,先在组内按性别分类排序(Map 端分区 + 排序),并计算每组男生 / 女生的身高总和(Combiner)。
- 然后把男生数据统一交给男生统计员(Reduce 0),女生数据交给女生统计员(Reduce 1)(Reduce 端 Fetch)。
- 统计员把所有小组交来的同性别数据合并排序(Reduce 端 Merge + Sort),最后计算平均值(Reduce 函数)。
六、MapReduce 与传统编程的区别
| 传统单机编程 | MapReduce 分布式编程 |
|---|---|
| 处理小数据,单进程运行 | 处理海量数据,多机器并行运行 |
| 开发者要处理所有逻辑(包括数据读写、计算) | 开发者只写 Map/Reduce 函数,框架处理分布式细节 |
| 一台机器故障,程序直接中断 | 某台机器故障,框架自动重试任务 |
953

被折叠的 条评论
为什么被折叠?



