概念简述
简介
MapReduce是一种计算框架,计算模型用来解决海量数据计算问题。在运行时一部分负责管理叫MRAppMaster运行在yarn容器中,剩下的统称为任务task也是在yarn容器中运行。
MR分为Map阶段和Reduce阶段,Map读取hdfs中的数据经过处理后交给Reduce进行处理将结果存入hdfs中。实际工作中我们只需要实现map,reduce,dirver阶段的工作,其他的读取传输排序组合由框架完成。
Map任务为一个java进程分布式运行在很多节点上,这样hdfs的海量数据就可以被很多的map任务处理。一个block对应一个map任务,比如hdfs数据由3个block组成就会有3个map任务来处理,这样可以并行进行数据处理。
Map处理的结果会交给Reduce任务,而一般map任务和reduce任务不在同一个节点,map通过网络将数据传输给reduce,map和reduce分布执行不同的逻辑。Map读取hdfs数据是按照一行一个<k1,v1>键值对的形式进行读取的,K1为字节顺序,v1为一行的内容。
例
文件hello
twofour
one two three
map函数
public void map(k1,v1,context){
context.write(k2,v2);//执行map处理逻辑
}
Hdfs的文件hello中的会被解析为<0,two four>和<9,one two three>,第二行的第一个字节顺序是9。
框架会对map输出的<k2,v2>进行排序和分组。排序是按照k2进行排序,分组将k2相同的v2分到一组,整个排序分组不会改变<k2,v2>的数量。排序分组完成后交给reduce处理,有的时候没有reduce阶段那么map的输出就直接写入到hdfs中。
Reduce函数
publicvoid reduce(k2,v2s,context){//v2s就是k2相同的v2的集合
context.write(k3,v3);//执行reduce逻辑
}
在复杂情况下map节点会向不同的reduce阶段输出数据
执行过程:
Map阶段:
1:框架使用inputFormat的子类把输入文件或目录下的所有文件(不支持递归)划分为很多的inputsplit,默认一个block对应一个inputsplit,一个inputsplit对应一个map任务。然后通过recordReader把每个inputsplit解析为很多的<k,v>键值对,一行一个键值对。
2:框架调用Mapper类中的map函数进行逻辑处理我们可以自己覆盖这个函数,参数返回值均为<k,v>键值对。
3:若无reduce则直接写入hdfs中,结束。
有reduce框架会对<k,v>键值对进行分区,不同分区的键值对会输从给不同的reduce,默认一个分区。
4:对分区中的键值对进行排序分组。
5:在map节点上可以执行reduce归约
6:将排序分组后的键值对存入到linux磁盘上。
Reduce阶段
1:框架会将多个map任务的输出按照不同的分区拷贝到不同的reduce节点上,这个过程叫做shuffle。
2:框架会对reduce接收的多个map的相同分区的<k,v>键值对进行合并。
3:框架调用reducer类的reduce方法,入参<k2,{v2}>输出<k3,v3>,一个<k2,{v2}>调用一次reduce函数我们可以覆盖这个函数实现自己的逻辑。
4:框架将reduce的输出保存到hdfs中,每个reduce任务输出一个文件,整个MR过程会输出一个目录,但是框架不能把目录下的文件合并。
下面是一个很简化的流程图,数据从左往右走
Shuffle过程简介
1. MAP任务处理完毕之后先将数据存入内存。
2. 紧接着对内存中的数据执行分区排序操作并存入磁盘,此时磁盘中就存在很多的小片用于存储。(写1)
3. 读取磁盘上小分片中的数据然后merge写入一个大文件,这个大文件中包含很多分区。(读1+写2)
4. 此时磁盘上会有很多的大文件,每个文件上的分区数据会输出给不同节点上的reduce任务(读3),这步操作应该还会产生IO消耗。
5. Reduce任务所在节点读取完毕后再对其进行merge操作形成一个大文件写入磁盘(写3)。
6. 最后reduce任务读取这个大文件作为自己的入参来进行计算。
以上就是hadoop中shuffle的简单过程,统计一下读写磁盘共有6次。Spark也有shuffle过程但是全部是基于内存的所以比hadoop快。