MapReduce 图解流程超详细解答(1)-【map阶段】

在MapReduce中,一个YARN  应用被称作一个job, MapReduce 框架提供的应用,master的一个实现被称作MRAppMaster

MapReduce Job的时间线

Timeline MapReduce Job


MapReduce Job  运行的时间线:

  • Map Phase:若干 Map Tasks 被执行
  • Reduce Phase: 若干Reduce Tasks 被执行

reduce可能会在map阶段结束之前开始执行,因此上面显示的有重叠的地方。

Map Phase

现在我们集中考察map相,一个关键的问题是一个应用需要多少map任务去运行现在的这个job

用户给了我们什么?

我们退回到之前的一步,当一个用户提交一个应用的时候,若干信息被提供给了YARN ,分别是:


  • 一个配置:这可以是一部分的,因为一些参数不需要用户特别指定,可以有自己的默认值。
  • 一个jar文件,含有一个map,一个combiner,一个reduce
  • 一个输入和输出信息 输入目录 是不是在hdfs上,有多少文件呢?输出的时候,我们存储在哪里

The number of files inside the input directory is used for deciding the number of Map Tasks of a job.
那么,输入的目录中文件的数量决定多少个map会被运行起来

多少个map任务?

应用针对每一个分片运行一个map,一般而言,对于每一个输入的文件会有一个map split。如果输入文件太大,超过了hdfs块的大小(64M)那么对于同一个输入文件我们会有多余2个的map运行起来。下面是FileInputFormat class 的getSplits()的伪代码: 

num_splits = 0
for each input file f:
   remaining = f.length
   while remaining / split_size > split_slope:
      num_splits += 1
      remaining -= split_size

where:

split_slope = 1.1 分割斜率
split_size =~ dfs.blocksize 分割大小约等于hdfs块大小

在mapreduce2.0以上版本mapreduce.job.maps 属性会被忽略

MapTask Launch
启动MapTask

mapreduce应用会向资源管理器请求这个job需要的容器,一个maptask容器请求每一个maptask。一个容器对每一个maptask的请求会尝试利用map分片的本地性,应用会请求一下数据:

  • 请求map split 和container在同一个节点管理器的container 
  • 如果没有,请求一个map split 和container在同一个机架上的节点管理器上的container
  • 否则请求任意节点管理器上的container

这只是一小部分资源任务。资源任务器在资源任务器既定目标和指定目标冲突的时候,可以忽略本地性。当一个容器被分配一个任务,map就马上启动了。

Map阶段:一个执行阶段的例子

Map Phase execution

map 相的一个简要图:


  • 有两个节点管理器:每一个2GB的内存,每一个map需要1GB我们可以并行运行两个容器。这是最好的情况,而资源任务器的决策可能会有所不同
  • 集群没有其他的YARN任务运行
  • 我们的job有8个map分片,也就是在输入文件夹中有7个文件,只有一个是大于hdfs块大小的,需要被拆分为两个文件。

map任务的执行时间线

Map Task Execution Timeline

现在我们可以聚焦单个的map task:这是单个map的执行时间线:

  • 初始相:我们设置map任务
  • 执行相:map分片里面的每一个键值对进行map()函数运算
  • 溢写相:map的输出保存在环形内存缓冲区,当缓冲区满80%(一般80%),启动溢写相,将缓冲的数据写出到磁盘。
  • 洗牌相:在溢写相的结尾,我们合并多有的输出,并且打包他们以便进行reduce相处理。

map任务:初始化

在初始化阶段,我们:


  1. 创建一个上下文对象(context )(TaskAttemptContext
  2. 创建用户map.class实例
  3. 设置输入
  4. 设置输出
  5. 创建mapper的上下文(MapContext.classMapper.Context.class)
  6. 初始化输入也就是:
  7. 创建 SplitLineReader.class 分片行阅读器
  8. 创建HdfsDataInputStream.class hdfs数据输入流

Map任务:执行阶段

MapTask execution

执行阶段通过 Mapper class.的run()方法:

用户可以重写这个方法,但是默认的时候通常会调用setup而启动这个程序。这个函数默认并不做什么有用的 事情,但是可以被用户覆盖重写以便于设置任务(例如初始化类的变量),当设置完成之后,分片的每一个键值对会激发map()方法。因此map()接收到一个键,一个值,以及一个上下文context。使用这个上下文对象,一个map就会存储其输出到缓存中。

请注意,map分片是一个快一个块截取的(例如64kb),每一个快分割成为若干键值对的数据( SplitLineReader.class干的好事),这是在Mapper.Context.nextKeyValue内部完成的。当map分片被全部处理之后,run()会调用clean()方法。默认的,没有什么会被执行,除非用户重写覆盖他。


map任务:溢写阶段

Spilling phase


正如我们在执行阶段看到的一样,map会使用Mapper.Context.write()将map函数的输出溢写到内存中的环形缓冲区 (MapTask.MapOutputBuffer)。缓冲区的大小是固定的,通过mapreduce.task.io.sort.mb (default: 100MB)指定。
任何时候当这个缓冲区将要充满的时候(mapreduce.map. sort.spill.percent: 默认80% ),溢写将会被执行(这是一个并行过程,使用的是单独的线程,缓冲池还可以继续被写入)。如果溢写线程太慢,而缓冲区又忙了的话,map()就会暂停执行而等待。
溢写线程执行下面的动作:
  1. 创建一个溢写记录SpillRecord 和一个FSOutputStream 文件输出流(本地文件系统)
  2. 内存内排序缓冲中的块:输出的数据会使用快排算法按照partitionIdx, key排序
  3. 排序之后的输出会分割成为分区:每一个分区对应一个reduce
  4. 分区序列化写到本地文件
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MapReduce中进行数据清理可以分为多个阶段,第一阶段通常是数据清洗。下面简要介绍一下Java MapReduce实验中的数据清洗阶段。 数据清洗阶段的目的是将原始数据进行预处理,使得后续的计算能够更加准确和高效。具体而言,数据清洗阶段需要完成以下任务: 1. 去除无用数据:在数据中可能存在一些与计算无关的内容,比如注释、空行等,需要将这些内容去除。 2. 格式化数据:对于有些数据,可能存在格式上的不规范,需要将其进行规范化,比如日期格式、数字格式等。 3. 数据过滤:有些数据不符合计算要求,需要将其过滤掉。 4. 数据转换:有些数据需要进行转换,比如将字符串转为数字、将时间戳转为日期等。 下面是一个简单的Java MapReduce程序,用于进行数据清洗。该程序的输入为一个文本文件,包含多条记录,每条记录由多个字段组成,字段之间用逗号分隔。程序的输出为清洗后的数据集,每条记录仅包含需要的字段,并且字段之间用逗号分隔。 ```java public class DataCleanMapper extends Mapper<LongWritable, Text, NullWritable, Text> { private Text outputValue = new Text(); private StringBuilder builder = new StringBuilder(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); if (fields.length >= 5) { // 只保留前5个字段,其余字段丢弃 for (int i = 0; i < 5; i++) { builder.append(fields[i]).append(","); } builder.deleteCharAt(builder.length() - 1); outputValue.set(builder.toString()); context.write(NullWritable.get(), outputValue); builder.setLength(0); } } } ``` 该程序的主要逻辑如下: 1. 将输入的文本文件按行读取,每行数据作为一个键值对的value。 2. 根据逗号将每行数据分割成多个字段。 3. 判断字段个数是否大于等于5,如果是,则只保留前5个字段,其余字段丢弃。 4. 将清洗后的数据作为一个键值对的value,写入输出文件中。键使用NullWritable.get()表示不需要指定键,输出的value为Text类型,值为清洗后的数据。 该程序的输出结果为清洗后的数据集,每条记录仅包含需要的字段,并且字段之间用逗号分隔。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MrCharles

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值