MapReduce中map阶段和reduce阶段以及shuffle过程内部工作机制记录

  1. mapTask工作原理:

我们在写job任务时,指定一个FileInputFormat,设置一个路径,FileInputFormat类继承InputFormat(一个抽象接口),里面提供了一个抽象方法getSplits(),FileInputFormat中重写该方法的逻辑,对文件进行切分成多个split,然后通过RecordReader(默认使用lineRecordReader)按行进行读取文件内容并交给map去执行自己写的逻辑进行处理,被map处理之后的数据会交给OutputCollector收集器,并对结果中的key进行分区(默认按照对key进行hash操作),随后写入将结果写入内存缓冲区,每一个maptask都会有一个内存缓冲区,用来存放map的输出。当缓冲区快写满的时候,会在磁盘上创建一个临时文件,从缓冲区溢出的内容会写入到临时文件中,在整个maptask结束后,对磁盘中的临时文件进行merger合并,生成最终的正式文件,等待reducetask进行拉取。

  1. 详细步骤:

1.首先,读取数据的组件InputFormat(默认读取文本文件使用TextInputFormat)通过getSplits方法对输入目录的文件进行逻辑切片规划,默认按照128M的大小得到splits,有多少个split就对应启动多少的maptask。split的数量和block的数量可能是一对多,默认是一对一。

2.在得到文件切片的splits之后,在有RecordReader对象(默认lineRecordReader)进行读取,以‘\n’作为分隔符,每次读取一行数据,返回一个<key,value>,key表示每行首字符偏移值,value表示这一行的文本内容。

3.将读取到的<key,value>,交给自己继承的Mapper中,执行自己重写的map函数,RecordReader每读取一行,map方法都会调用一次

4.map阶段执行完之后,会将结果通过context.write进行collect收集,在collect中,会进行分区处理,默认使用hashPartitioner。

备注:mapreduce提供的partitioner接口,可以自己实现该接口完成分区的逻辑,可以根据key,value来确定当前的输出应该交给那个reducetask来处理,,默认使用hashpartitioner对key进行hash之后,再以reducetask的数量取模。

5.接下来,会将数据写入内存中,在内存中这片区域叫做环形缓冲区,其作用就是批量收集map结果,减少磁盘的IO影响,我们的key/value都会被序列化成字节数组,写入缓冲当中。

环形缓冲区:本质是一个数组,里面存放着key/value序列化的数据以及元数据信息,包括partition,key的起始位置,value的起始位置以及value的长度,

环形结构实质上是一个抽象的概念,缓冲区时候大小限制,默认100MB,当maptask的输出结果很多时,可能会撑爆内存,因此需要在一定条件下将缓冲区的内容写入到磁盘当中,从内存写入到磁盘中的过程叫溢出,由单独的线程来完成,不影响maptask的内容写入,一般当缓冲区的内容达到了整个区的百分之八十,就会触发溢写线程的启动,执行溢写过程。

6.当溢写线程启动之后,需要对百分之八十的空间内的key进行排序,这个过程是mapreduce模型默认的过程,如果我们在job设置过Combiner,那么现在就会使用该Combiner进行一次合并处理,将相同key的key/value中的value加起来。减少溢写到磁盘的数据量。

7.每一次的溢写都会在磁盘上生成一个临时文件,如果map的输出特别大,相应的就会存在多个溢写文件,这些文件只在磁盘上临时存在,当整个数据处理完成之后,将开始对磁盘中的临时文件进行merge合并,因为最终的文件只有一个,写入磁盘,并且为这个文件提供一个索引文件,用于记录每个reduce对应数据的偏移量,至此,map阶段结束

 

  1. reduceTask工作原理:

Reduce阶段大致分为三个阶段,copy,sort,reduce,在copy阶段包含一个eventFetcher来获取已完成的map列表,由fetcher线程去copy数据,在此过程中会启动两个merge线程,分别在inMemoryMerge和onDiskMerger,分别将内存中的数据merge到磁盘,和将磁盘中的数据进行merge。待数据copy完成之后,copy阶段就完成了,然后开始排序,主要是执行finalMerge操作,纯粹的sort阶段,完成之后就是reduce阶段,调用用户自定义的reduce函数处理数据

  1. 详细步骤:

1.copy阶段,reduce会启动一些数据copy的线程(fetcher),通过HTTP方式请求maptask获取属于自己的文件,

2.Merge阶段,这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值,copy过来的数据会先放入内存缓冲区,这里的缓冲区大小要比map端的更为灵活,merge有三种形式:内存到内存,内存到磁盘,磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量达到一定阈值,就会启用内存到磁盘的merge,与map端类似,这也是溢写的过程,如果我们设置了combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件,第二种merge方式会一直在运行,直到没有map端的数据才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的文件。

3.把分散的数据合并成一个大的数据后,还会对合并后的数据排序。

4.对排序后的数据调用reduce方式,键相同的键值对调用一次reduce方式,执行其逻辑,最后把这些输出键值对写入到指定的位置。

 

  1. shuffler机制

map阶段处理的数据如何传递给reduce阶段,是mapreduce框架中最关键的一个流程,这个流程叫做shuffle

shuffle是mapreduce的核心,它分布在mapreduce的map阶段和reduce阶段。一般把从map产生输出开始到reduce 取得数据作为输入之前的这个阶段称作shuffle。

1.collect阶段:将maptask的结果输出到大小为100M的环形缓冲区,保存的是key/value的键值对,partition信息等。

2.spill阶段(溢写):当内存中的数量达到一定的阈值的时候,就会将数据写入本地磁盘,将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了combiner,还会将相同的分区和key的数据进行合并

3.merge阶段:将所有的溢写的临时文件进行合并,以确保一个maptask最终只会产生一个中间数据文件。

4.copy阶段:reducetask启动fetcher线程去已经完成maptask的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阈值,就会将数据写到磁盘之上,

5.merge阶段:在reducetask远程复制数据的同时,会在后台开启两个线程对内存到本地的数据进行合并操作。

6.sort阶段:在对数据进行合并的同时,会进行排序操作,由于maptask阶段已经对数据进行了局部的排序,reducetask只需要保证copy的数据的最终整体有效性即可,shuffle中的缓冲区大小会影响到mapreduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越小,执行速度就越快,缓冲区的大小可以通过参数进行调整,参数:io.sort.mb默认100M

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值