Hadoop原理之MapReduce运行机制

什么是MapReduce?

MapReduce本身是一种编程思想,它将处理数据分为两步,第一步是Map阶段,即映射阶段,第二步是Reduce阶段,即聚合阶段。这一革命性的思想是谷歌最先提出的,之后诞生的hadoop也运用了这一思想。因此有了基于Hadoop的mapreduce的分布式计算框架,例如后续流行spark和flink也是基于MapReduce思想而开发出的分布式计算框架。

MapReduce的好处

mapreduce分布式计算框架的最大好处就在于计算性能优越。因此在大数据行业,mapreduce是重中之重,在数据量日益庞大的今天,如何高效快速的计算大规模的数据是企业面临的一大难题。通过mapreduce思想,将处理数据的过程同时运行在多台机器上,即减轻了单一机器的运算压力,也提高了数据的处理效率,最后将多台机器计算出的结果整合起来,就得到了最终的计算结果。

mapreduce在yarn上的运行生命周期

在这里插入图片描述
在程序中,都会调用waitforcompletion()方法,以显示程序运行的全过程
1.客户端运行程序,运行到job.waitforcompletion()方法后,此时这个方法中会调用localJobRunner方法,接着再调用其中的run方法,接着就是调用runMapTask和runReduceTask方法。首先会调用runMapTask去调用MapTask,此时MapTask执行需要一个容器,所以客户端会去向RM发起请求,请求执行自己的程序
2.请求之后,RM会返回给客户端一个jobID,同时还会返回一个路径,路径为/tmp/hadoop-yarn/staging/root/.staging,告知客户端,让客户端将其要运行的jar包的各种相关信息,如程序源文件、配置文件等全都放入该路径下
3.然后客户端就会去hdfs中查看读取目录中的文件,并计算任务切片
划分任务切片规则:假设要读取的文件有两个,a.txt为130M,b.txt为200M,此时设置的默认任务切片大小为128M,当文件大小>=128M1.1时,此时就将该文件的前128M划分为一个任务切片,若剩余文件大小不满128M,就将该文件剩余的文件划分为第二个任务切片,但当文件大小<=128M1.1时,此时就直接将该文件划分为一个任务切片;这样做是为了防止资源浪费,所以此时第一个任务切片为a.txt的130M,第二个任务切片为b.txt的128M,第三个任务切片为b.txt的129M-200M
计算完成后,所有任务切片先被放在一个ArrayList中,然后又被存放到一个数组 FileSplit[ ] 中,这个数组会返回给客户端,返回的同时,这个任务切片数组又会被客户端存到RM给定的目录下(此过程为序列化),并命名为job.split,hdfs会在RM给定目录下再以jobID为名创建一个文件夹,此程序的所有数据都存入该文件夹中
4.与此同时,还会将要运行的程序的jar包也存到这个目录中,并且重命名为job.jar
5.又因为程序中自定义的Job类对象job中定义了许多的Class文件信息还有一些配置文件信息,这些信息在后续程序运行过程中还会被用到,所以也会被序列化存储到该路径下,并且命名为job.xml
6.此时任务切片、程序jar包、程序参数,全都已经存储到指定目录中,客户端就会向RM发送请求,请求一个容器(默认1.5G,1核)来执行自己的程序,完成需求。要知道,RM中一直维护着一个 容器分配任务队列,这个队列中在排队的全都是一个个需求对象,即有许多客户端都在向RM请求执行程序,所以程序会进行排队。与此同时,每个NM都在持续给RM发送心跳和汇报数据,NM主要都三件事要做:1)告诉RM自己还“活着”,2)向RM汇报自己资源的使用情况,3)接收属于自己的程序任务。RM还会给 任务队列中的每个任务都打上标签,代表这个程序由哪个NM来执行
7.当NM发送心跳过后,若发现有分配给自己的任务,则这个任务就会被NM领走,任务送到NM后,NM会根据任务内部携带的信息,创建对应大小的容器,此时创建的容器大小为 2G,1核,因为内存默认大小为1G,不存在小数点,所以直接给了2G
8.容器创建好之后就要开始执行程序,每个容器都有一个工作目录,这时容器会去刚才存放信息的目录中拉取所需要的文件到本机的工作目录中,进而执行程序
9.在上述8过程中,客户端一直会持续请求RM,询问容器是否分配成功,分配成功后,客户端会发送一个shell指令到对应的NM,并且告诉NM这个指令属于该NM的哪一个容器,命令为,“java -cp …MRAppMaster…”,此时对应的容器中就会有一个进程,名为MRAppMaster,此时MRAppMaster就会读取本机的工作目录,得知此job该执行几个maptask和reducetask
10.然后MRAppMaster就会向RM去申请容器,会指明申请几个容器,并且大小和核数是多少,这个申请又会被放到任务队列中,直到RM分配给NM所需的全部容器。后续过程中虽然是MRAppMaster在操作,但是客户端与MRAppMaster的通信也是一直连接的,MRAppMaster会一直报告程序的执行进度,所以客户端才可以打印出许多进度日志
11.此时申请的容器已经全部生成,这时新生成的每一个容器也都会去拉取执行程序所需的文件,放入到本机的工作目录中
要注意,MRAppMaster这个容器是客户端向RM申请的,这个容器可能会在任何一台机器上,后续分配的多个容器都是MRAppMaster向RM申请的,第9步完成后,后续的操作都是MRAppMaster在操作,和客户端没有关系了
12.此时MRAppMaster就会向各个容器发送指令,在每个容器上启动YarnChild,然后有一部分YarnChild内运行的是maptask,另一部分YarnChild内运行的是reducetask,并且每个task后还会加上编号,如mt0,mt1等等,此时reducetask会等待,直到maptask执行完毕才开始执行
13.由于每个task都会读取工作目录中的文件信息,会得知去哪个目录中找哪个文件的哪一部分进行读取,读取到数据后就会开始执行程序,由于文件大小不均匀,所以每个task跑完的时间不同,当一个task跑完之后,这个容器便会释放,当所有容器中的任务都跑完后,容器全都释放,此时MRAppMaster也没有存在的必要,这个容器也会释放

三个阶段

读数据阶段

在这里插入图片描述
在上述执行map task的容器中,map task是由YarnChild调用的,YarnChild会调用用户自定义类xxMapper中的run方法,在run方法中先执行setup方法,然后开始一个while循环,这个while循环的条件是判断下一对kv数据是否存在,此时context.netKeyValue每执行一次,就会去调用一次LineRecordReader中的readLine方法,然后就会去对应的任务切片中读取数据,若还能读取到一行数据,证明kv数据还能读到,因此将读取到的数据按照offset,value的形式作为参数传给map,此时开始执行map方法,map方法中执行的是处理数据的逻辑,当map方法执行完一次之后,while循环会继续执行,就又会去任务切片中读取数据,直到数据全部读取完成,也就意味着数据全部都通过map方法处理完成

map task阶段

当数据经过map处理之后,全部输出为一个个的kv形式的数据,此时maptask还远未结束,这些数据会进入一个环形缓冲区
在这里插入图片描述
在kv数据进入环形缓存区之前,会先将这些数据按照key进行分区,执行context.write()之后,这些数据会传到HashPartioner中的getPartition方法中,这个方法会计算key的hashcode值,然后 &Integer.MAX_VALUE ,这样即可保证 hashcode % numReduceTasks 后的值必为正数,例如指定两个reduce 任务,此时就用(key.hashcode()&Integer.MAX_VALUE )%2,就会将全部数据分到0区和1区中,此时每一对kv数据都会被打上分区编号,在这之后,这些kv数据才会被存储到环形缓存区中(环形缓存区在另外文章中细讲)
环形缓存区的默认大小为100M,在环形缓存区中会存入许多数据,主要是kv数据和其元数据,当这些数据存储的总大小达到环形缓存区大小的80%时,此时环形缓存区就会发生溢出,控制溢出的是另一个程序,叫Spiller
在这里插入图片描述在数据溢出之前,Spiller还会对这些数据做一个区内排序,默认调用的是QuickSort方法进行排序,此处的排序规则可以自定义。当排序完成后,数据就会开始溢出,溢出的数据会分区存储,保存到本地磁盘的一个文件中,这是个小文件,每次环形缓存区中的数据达到80%后,都会执行一次数据溢出,所以map task任务中可能会产生多个溢出数据的小文件,当所有数据全部经过环形缓存区后,所有的数据都被分区保存到了不同的小文件中,此时又会有一个程序,即merger,会将所有的这些小文件进行合并
在这里插入图片描述
merger会将所有的小文件都拉取过来,然后读取所有小文件中的数据,做一个归并排序,将所有数据进行排序,并且分区保存,最后存储到本机器的本地磁盘中,提供http协议供外界访问

reduce task阶段

所有maptask执行完成后都将数据保存到了其本地磁盘中,此时轮到reduce task任务开始执行,reduce task 中有一个拉取对应数据的程序,即Fetcher,这个程序会将与该reducetask对应的分区数据全都通过网络拉取过来,即此reducetask的编号为0,则将所有maptask结果中0区的数据拉取过来做聚合,编号为1的reducetask就将所有1区的数据拉取过来。上述数据分发的过程叫做shuffle
在这里插入图片描述

所有maptask产生的0区数据都会被拉取到reducetask_0中,此时拉取过来的是又是多个小文件,当所有数据拉取完成后,这里还要再做一个归并排序,即使用merger对所有小文件进行排序合并操作
在这里插入图片描述
使用merger 对拉取来的所有数据进行一次 归并排序 操作,将所有kv数据按照key进行排序,再进行分组,最后将每个分组的数据存到迭代器中
在这里插入图片描述

当迭代器去取值时,会使用GroupingComparator中的compare()方法去判断两个kv 的k是否相同,相同的话才会放入同一个迭代器中,当判断到两个kv的k不相同时,就会使用reset()方法重新获取新的key进行判断,要知道,reduce task调用重写的reduce方法,再调用其中的run方法,进而调用run方法中被while包括的reduce方法,本来中的reduce方法中是存在for循环的,用来遍历迭代器中的每个数据,因此此处有一个for循环,还有一个while循环,reduce读取到迭代器中的数据,对其进行聚合逻辑运算,然后使用OutputFormat组件将数据输出,一般默认为TextOutputFormat,再通过TextOutputFormat拿到一个LineRecordWriter,reduce处理后的结果会传给LineRecordWriter,调用其中的write(key,value)方法,可以通过配置write方法中的fs.defaultFS属性来指定输出目的地,以 key.toString() \t value.toString()的形式,将结果输出到本地或者hdfs等文件系统中

mr的特点

在这里插入图片描述

结语:为使论述方便,上述过程可能存在某些细节上的疏忽,望海涵

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值