MapReduce中的Map任务是整个计算过程的第一阶段,其主要工作是将输入数据分片并进行处理,生成中间键值对,为后续的Shuffle和Sort阶段做准备。
InputFormat
负责将输入数据划分成若干个InputSplit
,每个InputSplit
对应一个Mapper
任务的输入。InputSplit
的目的是为了充分利用集群中的计算资源,并实现数据的并行处理。RecordReader
,从输入InputSplit
中解析出一个个key/value
。Master
节点(JobTracker
)会将Map
任务分配给空闲的Map任务槽(Task Slot
)。Map
任务槽都运行在集群中的某个节点上,并且能够处理一个或多个Mapper
任务。Map
任务被分配到一个节点上时,该节点会启动一个Mapper
实例。Mapper
实例会首先执行初始化操作,包括获取输入数据的位置信息、加载用户自定义的Map函数等。Mapper
开始处理其对应的InputSplit
中的数据。Mapper
会调用用户定义的Map函数,该函数将输入记录转换成若干个中间键值对(key-value pairs
)。在Map
任务中,为了提高处理速度和效率,通常会采取一些数据处理优化策略,比如:
数据局部性优化:尽可能在处理数据时减少网络通信开销,使得处理同一输入分片的数据的Mapper
任务能够在同一个节点上执行,以减少数据的传输成本。
流水线处理:Map
任务可以通过流水线处理来提高吞吐量,即在处理一个输入记录的同时,可以开始处理下一个输入记录,从而减少处理过程中的等待时间。
Map任务通常会将中间结果缓存在内存中,但如果缓存空间不足以存储所有的中间结果时,会采取一些策略来管理缓存,例如:
溢出到磁盘:当内存中的中间结果达到一定阈值时(比如默认的80%),Map任务会将部分中间结果写入磁盘的临时文件中,以释放内存空间,从而继续处理新的输入记录。
内存管理算法:Map
任务可能采用LRU
(最近最少使用)等算法来管理内存中的中间结果,保留最常使用的数据,释放不常用的数据。
在 Map 阶段完成后,中间结果会被写入本地磁盘,但在写入之前,通常会进行本地排序操作。
本地排序可以确保相同 key
的数据在同一个位置,以便后续的 Shuffle
阶段更高效地进行数据传输和处理。
在必要时,还可以对数据进行合并和压缩操作,以减少存储空间和提高数据传输效率。这些步骤都是为了优化整个 MapReduce
作业的性能和效率。
溢写阶段排序详情: 利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition
进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
在Map
任务执行期间,Master
节点会周期性地接收来自Map
任务的心跳信息,以报告任务的运行状态,并定期更新任务进度。
如果Map
任务长时间没有发送心跳信息,Master
节点可能会将其标记为失败,并重新分配任务给其他节点执行。
Map
任务在执行完所有的输入记录后,会向Master
节点报告任务完成,并将生成的中间结果的位置信息发送给Master
。
MapReduce
框架具有强大的容错机制,即使Map
任务在执行过程中出现失败,Master
节点也能够重新分配任务并继续执行,以确保作业的顺利完成。
当所有数据处理完成后,MapTask
对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
当所有数据处理完后,MapTask
会将所有临时文件合并成一个大文件,并保存到文件output/file.out 中,同时生成相应的索引文件output/file.out.index。
在进行文件合并过程中,MapTask
以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并mapreduce.task.io.sort.factor(默认 10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。
让每个 MapTask 最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。
一旦所有的输入记录都被处理完毕,并且中间结果都被写入磁盘,Map
任务就会结束。
Map
任务会将最终的中间结果的位置信息发送给Master
节点,以便后续的Shuffle
和Sort
阶段能够获取到这些数据。
假设我们有一个大的文本文件,其中包含了多篇文章,每篇文章之间由一个或多个空行分隔。Map任务的目标是将输入数据中的每个单词映射成键值对(单词, 1),以便后续的Reduce任务可以统计每个单词的频次。
(1) 输入数据的划分:
Hadoop
中,这个文本文件被分成若干个逻辑块(block
),每个逻辑块会被存储在HDFS
的不同节点上。当我们提交MapReduce
作业时,Hadoop
会将这些逻辑块划分成若干个InputSplit
,每个InputSplit
对应一个Mapper
任务的输入。(2) Map任务的启动:
MapReduce
作业被提交,Master
节点会启动作业的第一个阶段,即Map
阶段。Master
节点会根据集群中的可用资源情况,将Map
任务分配给空闲的节点上的Map
任务槽。(3) Mapper的初始化:
每个Mapper
任务在运行之前都需要进行初始化。这个初始化过程包括获取对应的InputSplit
的数据位置信息、加载用户自定义的Map
函数等。
在我们的例子中,Map函数需要额外的逻辑来识别文章的标题。
(4) 数据处理:
Mapper
开始处理其对应的InputSplit
中的数据。对于每个InputSplit
,Mapper
会逐行读取数据。Mapper
会识别每篇文章的标题,并为每篇文章的每个单词生成键值对。对于每个键值对,键是单词,值是1,表示该单词在文章中出现了一次。(5) 中间结果的缓存:
Mapper
会将生成的中间键值对缓存在内存中。当内存中的数据达到一定阈值时,部分结果会被写入磁盘的临时文件中以释放内存空间。(6)任务状态更新:
Map
任务执行期间,Mapper
会定期向Master
节点发送心跳信息,以报告任务的运行状态和进度。Master
节点会根据这些信息来监控任务的执行情况,并在必要时重新分配任务。(7) Map任务的结束:
Mapper
处理完其对应的InputSplit
中的所有数据,并且中间结果都被写入磁盘后,Map任务结束。Mapper
会将最终的中间结果的位置信息发送给Master
节点,以便后续的Shuffle
和Sort
阶段能够获取到这些数据。通过Map任务的执行,我们得到了每篇文章中单词的频次统计结果,并且识别出了每篇文章的标题。这些中间结果将被用于后续的Shuffle和Sort阶段,最终得到我们想要的每篇文章中单词的频次统计结果。