详细讲解MapReduce过程

关于整理

此文百分之七十摘自我认为讲的很清楚的博客,我都贴了地址,很感谢这些博主的无私奉献!我再将一些自己的实例代码和知识点的补充加入进去,希望能更好的理解mapreduce的整个过程。

从启动和资源调度来看MapReduce过程

首先-先了解一下必知概念

From:MapReduce工作原理图文详解JobTracker和TaskTracker概述

  • 客户端(Client):编写mapreduce程序,配置作业,提交作业,这就是程序员完成的工作;
  • JobTracker:JobTracker是一个后台服务进程,启动之后,会一直监听并接收来自各个TaskTracker发送的心跳信息,包括资源使用情况和任务运行情况等信息。 
    • 作业控制:在hadoop中每个应用程序被表示成一个作业,每个作业又被分成多个任务,JobTracker的作业控制模块则负责作业的分解和状态监控。
    • 状态监控:主要包括TaskTracker状态监控、作业状态监控和任务状态监控。主要作用:容错和为任务调度提供决策依据。
    • JobTracker只有一个,他负责了任务的信息采集整理,你就把它当做包工头把,这个和采用Master/Slave结构中的Master保持一致
    • JobTracker 对应于 NameNode
    • 一般情况应该把JobTracker部署在单独的机器上
  • TaskTracker:TaskTracker是JobTracker和Task之间的桥梁。TaskTracker与JobTracker和Task之间采用了RPC协议进行通信。 
    • 从JobTracker接收并执行各种命令:运行任务、提交任务、杀死任务等
    • 将本地节点上各个任务的状态通过心跳周期性汇报给JobTracker,节点健康情况、资源使用情况,任务执行进度、任务运行状态等,比如说map task我做完啦,你什么时候让reduce task过来拉数据啊
    • TaskTracker是运行在多个节点上的slaver服务。TaskTracker主动与JobTracker通信,接收作业,并负责直接执行每一个任务。
    • TaskTracker都需要运行在HDFS的DataNode上
  • HDFS:保存作业的数据、配置信息等等,最后的结果也是保存在hdfs上面 
    • NameNode: 管理文件目录结构,接受用户的操作请求,管理数据节点(DataNode)
    • DataNode:是HDFS中真正存储数据的
    • Block:是hdfs读写数据的基本单位,默认64MB大小,就是说如果你有130MB数据,那就要分成三个block,两个存放64MB,最后一个存放2MB数据,虽然最后一个block块是64MB,但实际上占用空间为2MB
    • Sencondary NameNode:它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间,在文件系统中设置一个检查点来帮助NameNode更好的工作。它不是要取代掉NameNode也不是NameNode的备份。可参考Secondary NameNode:它究竟有什么作用?

其次-走一遍流程

规范是:

  1. 执行的步骤 
    • 步骤中涉及的知识点 
      • 知识点的补充

按照这样的规范进行讲解

  1. 在客户端启动一个作业。拿个比方说,我提交了一个hive程序

    • 客户端(Client):编写mapreduce程序,配置作业,提交作业,这就是程序员完成的工作;
  2. 向JobTracker请求一个Job ID,就像你排队买车一样,你不得摇个号啊,没有这个号你就不能买车(执行任务)。

    • JobTracker:JobTracker是一个后台服务进程,启动之后,会一直监听并接收来自各个TaskTracker发送的心跳信息,包括资源使用情况和任务运行情况等信息。 
      • 作业控制:在hadoop中每个应用程序被表示成一个作业,每个作业又被分成多个任务,JobTracker的作业控制模块则负责作业的分解和状态监控。
      • 状态监控:主要包括TaskTracker状态监控、作业状态监控和任务状态监控。主要作用:容错和为任务调度提供决策依据。
      • JobTracker只有一个,他负责了任务的信息采集整理,你就把它当做包工头把,这个和采用Master/Slave结构中的Master保持一致
      • JobTracker 对应于 NameNode
      • 一般情况应该把JobTracker部署在单独的机器上
  3. 将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的文件夹中。文件夹名为该作业的Job ID。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息(Split)告诉了JobTracker应该为这个作业启动多少个map任务等信息。 
    • HDFS:保存作业的数据、配置信息等等,最后的结果也是保存在hdfs上面 
      • NameNode: 管理文件目录结构,接受用户的操作请求,管理数据节点(DataNode)
      • DataNode:是HDFS中真正存储数据的
      • Block:是hdfs读写数据的基本单位,默认64MB大小,就是说如果你有130MB数据,那就要分成三个block,两个存放64MB,最后一个存放2MB数据,虽然最后一个block块是64MB,但实际上占用空间为2MB
      • Sencondary NameNode:它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间,在文件系统中设置一个检查点来帮助NameNode更好的工作。它不是要取代掉NameNode也不是NameNode的备份。可参考[Secondary NameNode:它究竟有什么作用?
  4. JobTracker接收到作业后,将其放在一个作业队列里(一般来说,公司部门都与自己的队列,默认的调度方法是FIFO,也就是first in first out-队列),等待作业调度器对其进行调度,当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息(Split)为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和reduce槽。这里需要强调的是:map任务不是随随便便地分配给某个TaskTracker的,这里有个概念叫:数据本地化(Data-Local)。意思是:将map任务分配给含有该map处理的数据块的TaskTracker上,同时将程序JAR包复制到该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。 
    • TaskTracker:TaskTracker是JobTracker和Task之间的桥梁。TaskTracker与JobTracker和Task之间采用了RPC协议进行通信。 
      • 从JobTracker接收并执行各种命令:运行任务、提交任务、杀死任务等
      • 将本地节点上各个任务的状态通过心跳周期性汇报给JobTracker,节点健康情况、资源使用情况,任务执行进度、任务运行状态等,比如说map task我做完啦,你什么时候让reduce task过来拉数据啊
      • TaskTracker是运行在多个节点上的slaver服务。TaskTracker主动与JobTracker通信,接收作业,并负责直接执行每一个任务。
      • TaskTracker都需要运行在HDFS的DataNode上
  5. TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行,同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。

以提交一个完整的mapreduce任务来演示,这里用hive

hive> set mapred.job.queue.name=root.xxxxxxxxx;
    > insert overwrite table query_result
    > select * from A join B on A.name=B.name;    # 这里是提交一个job,这里提交hive任务

Query ID = xx_20161216130626_40fda9ef-386f-4ef3-9e13-7b08ad69118c  # 申请资源
Total jobs = 3
Launching Job 1 out of 3
Number of reduce tasks not specified. Estimated from input data size: 1   # reduce个数如果没有设置,那就根据split来确定
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapreduce.job.reduces=<number>
Starting Job = job_1481285758114_429080, Tracking URL = http://bigdata-hdp-apache500.xg01:8088/proxy/application_1481285758114_429080/
Kill Command = /usr/local/hadoop-2.7.2/bin/hadoop job  -kill job_1481285758114_429080
Hadoop job information for Stage-1: number of mappers: 2; number of reducers: 1
2016-12-16 13:06:39,518 Stage-1 map = 0%,  reduce = 0%
2016-12-16 13:06:50,846 Stage-1 map = 50%,  reduce = 0%, Cumulative CPU 3.7 sec
2016-12-16 13:06:55,987 Stage-1 map = 100%,  reduce = 0%, Cumulative CPU 11.24 sec
2016-12-16 13:07:13,418 Stage-1 map = 100%,  reduce = 67%, Cumulative CPU 12.53 sec
2016-12-16 13:07:28,715 Stage-1 map = 100%,  reduce = 82%, Cumulative CPU 16.31 sec
2016-12-16 13:07:58,252 Stage-1 map = 100%,  reduce = 100%, Cumulative CPU 17.7 sec
MapReduce Total cumulative CPU time: 17 seconds 700 msec
Ended Job = job_1481285758114_429080
Stage-4 is selected by condition resolver.
Stage-3 is filtered out by condition resolver.
Stage-5 is filtered out by condition resolver.
Moving data to: hdfs://mycluster-tj/tmp/hive-staging/hadoop_hive_2016-12-16_13-06-26_309_6781206579090170211-1/-ext-10000
Loading data to table test.query_result
Table test.query_result stats: [numFiles=1, numRows=3, totalSize=69, rawDataSize=66]
MapReduce Jobs Launched:
Stage-Stage-1: Map: 2  Reduce: 1   Cumulative CPU: 17.7 sec   HDFS Read: 13657 HDFS Write: 142 SUCCESS
Total MapReduce CPU Time Spent: 17 seconds 700 msec
OK
owntest.name    owntest.age owntest2.workplace  owntest2.name
Time taken: 96.932 seconds

---------------------
作者:哈士奇说喵 
来源:CSDN 
原文:https://blog.csdn.net/mrlevo520/article/details/76781186?utm_source=copy 
版权声明:本文为博主原创文章,转载请附上博文链接!

以上是在客户端、JobTracker、TaskTracker的层次来分析MapReduce的工作原理的,下面我们再细致一点,从map任务和reduce任务的层次来分析分析吧。

从Map,Reduce,Shuffle理解

总体步骤

这里写图片描述

也就分成input,split,map,shuffle,reduce五个步骤,这里我们按照例子来简单说明下,再详细的说明我会放在后面的细分部分,这部分作用是了解下mapreduce的怎么个流程。这里是一个wordcount的过程了

  1. input:也就是数据存储位置,这里当然是类似于hdfs这样的分布式存储啦,

  2. split:因为map task只读split,而split基本上和hdfs的基本存储块block同样大小,一个split对应一个map,你可以把它当做map的单位块来理解,投喂进map的时候必须要这样的格式,打个比方,比如只收硬币的地铁站,你只能投放1元硬币的,你投什么五毛,一角的,都是犯法的!对,警察叔叔就是他!

  3. map:这里做的是wordcount,而map程序是由程序原来编写的,如果非要用代码来写,我用python写你别打我

    <span style="color:#000000"><code class="language-python">splitdata = <span style="color:#009900">'Deer Bear River'</span> 
    aftermap = map(<span style="color:#000088">lambda</span> x:(x,<span style="color:#006666">1</span>),splitdata.split(<span style="color:#009900">" "</span>))
    <span style="color:#000088">print</span> aftermap
    
    
    <span style="color:#880000"># [('Deer', 1), ('Bear', 1), ('River', 1)]</span>
    </code></span>

    三个map对应三组Split,我这里只取了其中一组,就是这么个意思,组成key/value键值对

  4. shuffle:这是一个比较核心的过程,shuffle有洗牌的意思,这里的意思你把她理解成在拉斯维加斯赌场发牌的小姐姐,但是这个小姐姐并不是随机发牌,而是把红桃发给A先生,黑桃都发给B先生,诸如此类。如果非要说有什么套路,那么其中有一个HashPartitioner就帮我们做了hash,你把它想成低配版的聚类,狭义版聚类,因为这里的类特喵的必须是同一个key 啊!

  5. reduce:既然都说了似wordcount了,那我,额,额,做戏做全套,我还是用python来写这个过程

    <span style="color:#000000"><code class="language-python">shuffledata = [(<span style="color:#009900">'Deer'</span>, <span style="color:#006666">1</span>),(<span style="color:#009900">'Deer'</span>, <span style="color:#006666">1</span>),(<span style="color:#009900">'River'</span>, <span style="color:#006666">1</span>)]
    afterreduce_dict = {}
    <span style="color:#000088">for</span> i <span style="color:#000088">in</span> shuffledata:
       <span style="color:#000088">if</span> i[<span style="color:#006666">0</span>] <span style="color:#000088">not</span> <span style="color:#000088">in</span> afterreduce_dict:
           afterreduce_dict[i[<span style="color:#006666">0</span>]] = <span style="color:#006666">0</span>
       afterreduce_dict[i[<span style="color:#006666">0</span>]] +=<span style="color:#006666">1</span>
    
    <span style="color:#000088">print</span> afterreduce_dict
    
    
    <span style="color:#880000"># {'River': 1, 'Deer': 2}</span>
    </code></span>

    这个‘reduce’只是对传统意义上的取键值对统计而已,理解这层意思就行,不要纠结形式

总体的步骤就是这么5步,其实最玄妙的shuffle还没有拆开细讲,接下来,见证shuffle的时刻,百分之九十抄的(捂脸)

再细分

shuffle是什么?

除了map,reduce,中间部分就是shuffle,它横跨了map和reduce两端,可以说是核心过程

这里你只要清楚Shuffle的大致范围就成-怎样把map task的输出结果有效地传送到reduce端。也可以这样理解, Shuffle描述着数据从map task输出到reduce task输入的这段过程。你说我map task干完活了要输出数据了,然后接下来数据给哪个reduce?不能因为我和那个reduce关系好,我把所有输出结果都给它把,总要考虑下别的reduce的感受啊!那怎么分配?就有了shuffle过程!上面给了两张图,我都是盗来的,一张是官方的解释,一张是中文翻译,Tkanks-MapReduce:详解Shuffle过程MapReduce工作原理图文详解

shuffle解决什么问题?

在Hadoop这样的集群环境中,大部分map task与reduce task的执行是在不同的节点上。当然很多情况下Reduce执行时需要跨节点去拉取其它节点上的map task结果。如果集群正在运行的job有很多,那么task的正常执行对集群内部的网络资源消耗会很严重。这种网络消耗是正常的,我们不能限制,能做的就是最大化地减少不必要的消耗。还有在节点内,相比于内存,磁盘IO对job完成时间的影响也是可观的。从最基本的要求来说,我们对Shuffle过程的期望可以有:

  • 完整地从map task端拉取数据到reduce 端。
  • 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。
  • 减少磁盘IO对task执行的影响。

Map端分析

与上图步骤1,2,3,4一一对应来解释

  1. InputSplit:输入数据来源于HDFS的block,当然在MapReduce概念中,map task只读取Split,也就是是我们所说的分片。在进行map计算之前,mapreduce会根据输入文件计算输入分片(input split),每个输入分片(input split)针对一个map任务。 
    • Block是HDFS的基本存储单元,上文也有写,Block默认大小是64MB
    • Split是map task只读的单位,存储的并非数据本身,而是一个分片长度和一个记录数据的位置的数组
    • Split与block的对应关系可能是多对一,默认是一对一

Running map task:就是程序员编写好的map函数了,因此map函数效率相对好控制,而且一般map操作都是本地化操作也就是在数据存储节点上进行,这也就是上问所提到的数据本地化!

  • 数据本地化(Data-Local)。意思是:将map任务分配给含有该map处理的数据块的TaskTracker上,同时将程序JAR包复制到该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。

    1. partition:partition是分割map每个节点的结果,按照key分别映射给不同的reduce,也是可以自定义的。这里其实可以理解归类。我们对于错综复杂的数据归类。比如在动物园里有牛羊鸡鸭鹅,他们都是混在一起的,但是到了晚上他们就各自牛回牛棚,羊回羊圈,鸡回鸡窝。partition的作用就是把这些数据归类。只不过在写程序的时候,mapreduce使用哈希HashPartitioner帮我们归类了。默认是hash(key)%R哈希函数分散到各个reducer去 
      • 简单的hash做分配,就比如说我map task输出键值对(1,1),(2,1),(3,1), 我有三个reducer,假设我写个hash是除3取余的值为分配到的reducer位置(其实是要将key先变hash code再除的,我这里简略),那么(1,1)被分配到第一个reduce,(2,1)被分配到第二个reduce,(3,1)被分配到第三个reduce,同理再来个(4,1)被分配到第一个reduce,HashPartitioner的目的就是合理的分组策略将使得每个Reducer获得的计算负载差距不大,从而整体reduce的性能更加均衡。
      • 没理解HashPartitioner的胖友,可以参考下这个MapReduce 编程 系列十 使用HashPartitioner来调节Reducer的计算负载

Memory Buffer & 3:这是一个环形的缓冲区。map task输出结果首先会进入一个缓冲区内,这个缓冲区的大小是100MB,如果map task内容太大,是很容易撑爆内存的,所以会有一个守护进程,每当缓冲区到达上限80%的时候,就会启动一个Spill(溢写)进程,它的作用是把内存里的map task的结果写入到磁盘。这里值得注意的是,溢写程序是单独的一个进程,不会影响map task的继续输出。当溢写线程启动后,需要对这80MB空间内的key做排序(Sort)。排序是MapReduce模型默认的行为,这里的排序也是对序列化的字节做的排序。

​ Combiner:如果client设置过Combiner,那么现在就是使用Combiner的时候了。将有相同key的key/value对的value加起来,减少溢写到磁盘的数据量,combiner简单说就是map端的reduce!。Combiner会优化MapReduce的中间结果,所以它在整个模型中会多次使用。那哪些场景才能使用Combiner呢?从这里分析,Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。所以从我的想法来看,Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。Combiner的使用一定得慎重,如果用好,它对job执行效率有帮助,反之会影响reduce的最终结果。

  1. Merge:每次溢写会在磁盘上生成一个溢写文件,如果map的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个溢写文件存在。当map task真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件。最终磁盘中会至少有一个这样的溢写文件存在(如果map的输出结果很少,当map执行完成时,只会产生一个溢写文件),因为最终的文件只有一个,所以需要将这些溢写文件归并到一起。 
    • Merge是怎样的?比如“aaa”从某个map task读取过来时值是5,从另外一个map 读取时值是8,因为它们有相同的key,所以得merge成group。什么是group。对于“aaa”就是像这样的:{“aaa”, [5, 8, 2, …]},数组中的值就是从不同溢写文件中读取出来的,然后再把这些值加起来。请注意,因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,也会使用Combiner来合并相同的key。

至此,map端的所有工作都已结束,最终生成的这个文件也存放在TaskTracker够得着的某个本地目录内。每个reduce task不断地通过RPC从JobTracker那里获取map task是否完成的信息,如果reduce task得到通知,获知某台TaskTracker上的map task执行完成,Shuffle的后半段过程开始启动。

Reduce端分析

  1. Copy过程: Reduce会接收到不同map任务传来的数据,并且每个map传来的数据都是有序的。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求map task所在的TaskTracker获取map task的输出文件。因为map task早已结束,这些文件就归TaskTracker管理在本地磁盘中。 
    • 有人可能会问:分区中的数据怎么知道它对应的reduce是哪个呢?其实map任务一直和其父TaskTracker保持联系,而TaskTracker又一直和JobTracker保持心跳。所以JobTracker中保存了整个集群中的宏观信息。只要reduce任务向JobTracker获取对应的map输出位置就ok了哦。
  2. Merge: 这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:1)内存到内存 2)内存到磁盘 3)磁盘到磁盘。默认情况下第一种形式不启用,让人比较困惑,是吧。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。
  3. Reducer的输入文件。不断地merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。当Reducer的输入文件已定,整个Shuffle才最终结束。
  4. reduce阶段:和map函数一样也是程序员编写的,最终结果是存储在hdfs上的。

致谢

@上述所有链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值