MapReduce是一个由Google提出的编程模型,用于大规模数据集(大于1TB)的并行运算。它的核心思想是“分而治之”,将复杂的任务分解为多个简单的任务,然后并行处理,最后汇总结果。Hadoop开源项目实现了这一模型。
一、核心思想
MapReduce将计算过程抽象为两个主要阶段:
- Map(映射)阶段:将输入数据分割成独立的块,由多个Map任务并行处理。每个Map任务处理一个数据块,并输出一组中间键值对(key-value pairs)。
- Reduce(归约)阶段:将Map阶段输出的所有中间结果(具有相同key的value集合)进行汇总和计算,生成最终的结果。
简单来说,就是 “先分再合”。
二、为什么需要MapReduce?
在传统单机环境下,处理海量数据会遇到瓶颈:
- 硬件限制:单台机器的计算能力和存储空间有限。
- 时间成本:串行处理TB/PB级数据需要耗费极长的时间。
MapReduce通过分布式集群解决了这些问题:
- 并行处理:将任务分发到成百上千台普通服务器上同时计算。
- 数据本地化:尽可能将计算任务分配到存储有所需数据的节点上,减少网络传输开销。
- 自动容错:框架会自动处理节点故障、任务失败等情况,保证作业最终完成。
三、详细流程
一个完整的MapReduce作业(Job)执行流程如下图所示,其核心步骤可分解如下:
flowchart TD
A[输入数据] --> B[Split 0]
A --> C[Split 1]
A --> D[Split ...<br>数据分片]
B --> E[Map Task]
C --> F[Map Task]
D --> G[Map Task]
subgraph MapPhase[Map 阶段]
E --> H[(k1, v1)→<br>(k2, v2)列表]
F --> I[(k1, v1)→<br>(k2, v2)列表]
G --> J[(k1, v1)→<br>(k2, v2)列表]
end
H --> K[分区、排序、合并]
I --> K
J --> K
K --> L["Map端输出<br>(磁盘)"]
L --> M[Fetch & Merge<br>拉取相同分区的数据]
M --> N[分组 Group<br>(k2, [v2, v2, ...])]
N --> O[Reduce Task]
subgraph ReducePhase[Reduce 阶段]
O --> P["用户自定义Reduce函数<br>(k2, [v2, v2, ...])→(k3, v3)"]
end
P --> Q[输出文件 Part 0]
P --> R[输出文件 Part 1]
P --> S[输出文件 ...]
第1步:输入与分片(Input & Splitting)
- 输入数据:通常是存储在HDFS(Hadoop Distributed File System)上的大文件。
- 分片(Split):框架将输入数据自动切分成固定大小的数据块(例如128MB),每个分片由一个Map任务处理。分片是逻辑概念,并不实际切割文件。
第2步:映射阶段(Map)
- 每个Map任务读入一个分片。
- Map任务调用用户编写的
map()函数,逐行处理分片中的数据。 map()函数接收一个输入键值对(如:行偏移量作为key,一行内容作为value),处理后输出一组中间键值对。- 示例(词频统计):输入是
(行号, "hello world hello"),map函数输出("hello", 1),("world", 1),("hello", 1)。
- 示例(词频统计):输入是
第3步:洗牌阶段(Shuffle)
这是MapReduce的“心脏”,也是最复杂、最耗资源的一步。它连接了Map和Reduce阶段,主要完成以下工作:
- 分区(Partitioning):Map任务输出的每个键值对,会被发送到哪个Reduce任务呢?由分区器(Partitioner) 决定。默认的分区策略是對key的哈希值取模(
hash(key) % R,R是Reduce任务数),这样可以确保相同的key一定会被分配到同一个Reduce任务处理。 - 排序(Sorting):每个Map任务在处理完数据后,会先对输出结果在本地进行排序(按key排序),这样便于后续Reduce任务的处理。
- 溢出(Spilling):Map任务的内存缓冲区满后,会将排序后的数据写入本地磁盘。
- 合并(Combining)【可选优化】:这是一个本地化的Reduce操作。在数据溢出到磁盘之前,可以对具有相同key的value进行局部聚合(例如,在词频统计中,将
("hello", [1, 1])合并为("hello", 2))。这大大减少了网络传输的数据量。 - 抓取(Fetch):所有Map任务完成后,Reduce任务启动并通过HTTP协议从各个Map任务的磁盘上拉取(Fetch) 属于自己的那部分数据。
第4步:归约阶段(Reduce)
- 归并排序(Merge Sort):Reduce任务从多个Map任务抓取数据后,会先将这些数据文件进行归并排序,使得所有数据按key有序排列。
- 分组(Grouping):排序后,相同key的value会被分组到一起,形成
(key, [value1, value2, ...])的形式。 - 执行Reduce函数:Reduce任务为每一组数据调用用户编写的
reduce()函数。reduce()函数接收一个key和它的value迭代器,进行汇总计算后输出最终的键值对。- 示例(词频统计):输入
("hello", [2, 1])(来自两个Map任务),reduce函数求和,输出("hello", 3)。
- 示例(词频统计):输入
第5步:输出(Output)
- Reduce任务将最终结果写入指定的存储系统,通常是HDFS。每个Reduce任务会产生一个独立的输出文件(如
part-r-00000,part-r-00001)。
四、举例:词频统计(Word Count)
这是MapReduce的“Hello World”程序,用于统计文章中每个单词出现的次数。
- Input:文本文件被切分成多个分片。
- Map:每个Map任务逐行处理文本。
- 输入:
(偏移量, "to be or not to be") - 输出:
("to", 1),("be", 1),("or", 1),("not", 1),("to", 1),("be", 1)
- 输入:
- Shuffle:
- Combining(优化):Map本地合并,例如变为
("to", 2),("be", 2),("or", 1),("not", 1) - Partitioning & Sorting:对所有输出的键按字母排序,并分发给对应的Reduce任务。
- Combining(优化):Map本地合并,例如变为
- Reduce:
- 输入:
("be", [2, 1])(假设数据来自两个Map任务) - Reduce函数对value列表求和:
sum = 2 + 1 = 3 - 输出:
("be", 3)
- 输入:
- Output:将所有Reduce任务的输出合并,得到最终结果:
be 3 not 1 or 1 to 3
总结
| 阶段 | 输入 | 处理 | 输出 |
|---|---|---|---|
| Map | 数据分片 | 用户自定义的 map() 函数 | 中间键值对(k2, v2) |
| Shuffle | Map的输出 | 分区、排序、合并、网络传输 | 有序的(k2, list(v2))数据,按分区交给Reduce |
| Reduce | Shuffle后的数据 | 用户自定义的 reduce() 函数 | 最终结果(k3, v3) |
关键优点:
- 简单性:开发者只需关注业务逻辑(编写map和reduce函数),无需处理分布式并行计算、容错、数据分布等复杂问题。
- 可扩展性:只需增加服务器数量就能线性扩展计算能力,处理更庞大的数据集。
- 容错性:框架能自动重新执行失败的任务,确保作业成功完成。


872

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



