Hadoop之MapReduce的移动计算模型

版权声明:原创文章,未经允许不得转载。ps: 传统电商火热的时代已经成为过去 , 下一个阶段属于大数据 人工智能 , 服务、便捷、安全、效率、创新成为下一个阶段互联网时代的新词汇,而IT技术也随着行业的变化发展而不断更迭。对于码农的出路总结一句话:追技术不如追领域。[基础][设计][能力] https://blog.csdn.net/shengqianfeng/article/details/82797935

MapReduce

Hadoop核心组件MR-MapReduce

企业80以上使用MapReduce,未来可能替代Mapreduce的是spark,spark是2013年出现,时间短,技术储备不够是最大的原因。

 

MapReduce设计理念

  1. 移动计算,而不是移动数据
  2. 何为分布式计算

Hdfs的数据全部是分布式存放,而MapReduce使用的就是Hdfs上存放的数据。

 

计算框架MR

步骤1:split

         文件上传后变成一个个block块,mr切割block,得到很多数据片split0,split1,split2,….

数据片是按照键值对的格式输入给map线程执行的,map计算后输出的数据也是键值对格式。

 

步骤2:map

此步骤是根据具体需求做相应计算而自定义的JAVA程序,Map任务的个数由数据片splitn决定,一个map任务就是一个java线程,每一个map任务相互独立,每一个线程也是相互独立。每个线程中执行的JAVA对象相互独立,是同一java类。

 

步骤3:洗牌shuffle

以下内容摘录自:http://langyu.iteye.com/blog/992916便于理解:

 

Shuffle洗牌描述着数据从map task输出到reduce task输入的这段过程,每个map task都有一个内存缓冲区,存储着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘。

当整个map task结束后再对磁盘中这个map task产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task来拉数据。

 

以计算单词数量的WordCount为例,假设有8个map task和3个reduce task

细节:

        1 切割:在map Task执行的时候,它的输入数据来源于HDFS的block,当然在MapReduce概念里,map Task只读取split数据碎片。Split碎片与block的对应对应好关系可能是多对一,默认是一对一。假设map的输入数据都是像“aaa”的字符串。

 

        2 在经过mappper的运行后,我们得知mapper的输出是这样一个key/value:Key是“aaa”,value是数值1

        因为当前map端只能做加1的操作,在reduce Task里才去合并结果集。假设中一个job有3个reduce Task,到底当前的“aaa”应该交给哪个reduce去做?

        分区: MapReduce提供了Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的输出数据最终应该交由哪个reduce task处理。默认是对key进行hash后再对reduce Task的数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以定制并设置到job上。

         如果“aaa”进过Partitioner分区后返回0,则mapper的输出数据键值对就应该交给第一个reducer来处理。

接下来,需要将数据写入到内存缓冲区中,缓冲区的作用是批量表收集map结果,减少磁盘IO的影响。

Map task输出的key/value对及Partition的结果都会被写入缓冲区。当然在写入前key和value的值都会被序列化成字节数组。

 

        3 这个内存缓冲区是有大小限制的,默认是100MB。

        当map Task的输出结果很多时,就可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时存到磁盘文件中,然后map Task重新利用这块缓冲区内存空间。 

 

        溢写:这个从内存往磁盘写入数据的过程被称为Spill,中文翻译为溢写,字面意思很直观。

        这个溢写是由单独的线程来完成,称为溢写线程。不影响往缓冲区写map Task输出结果的线程。

当溢写线程启动时不应该阻止map Task线程结果的输出,所以整个缓冲区有个溢写的比例:splil.percent.这个比例默认0.8,也就是当前缓冲区数据已经达到阀值80MB,溢写线程启动,锁定这个80MB的内存,执行溢写过程。Map Task的输出结果还可以往剩下的20MB内存中写,互不影响。

 

        排序:当溢写线程启动后,需要对这80MB空间内的key做排序(Sort)。排序是MapReduce模型默认的行为,这里的排序也是对序列化的字节做的排序。

       

       合并:可以思考下,因为map Task的输出是需要发送到不同的reduce端去,而内存缓冲区没有对将发送到相同reduce端的数据做合并,那么这种合并应该是体现在磁盘文件中的。

 

        从官方图上也可以看到写到磁盘中的溢写文件是对不同的reduce端的数值做过合并。所以溢写过程一个很重要的细节在于,如果有很多key/Value对需要发送到某个reduce端去,那么需要将这些key/Value值拼接到一块,减少与分区Partition相关的索引记录。

        在针对每个reduce端而合并数据时,有些数据可能像:“aaa”/1,”aaa”/1。

        对于WordCount的例子就是简单统计出现的次数,如果在同一个map Task的结果中有很多像“aaa”一样出现多次的key,我们就应该把他们的值合并在一起,这个过程叫reduce,也叫combine。

        但是MapReduce术语中,reduce只是指reduce端执行从多个map Task取数据做计算的过程。除reduce外,非正式地合并数据只能算作combine了,其实大家都知道,MapReduce中将Combiner等同于Reducer。

        如果客户端设置过Combiner,那么现在就是使用Combiner的时候了。将有相同key的key/Value对的value加起来,减少溢写到磁盘的数据量。Combiner会优化MapReduce的中间结果,所以它在整个模型中会多次使用。

        那么哪些场景才能使用Combiner呢?

        分析,Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。所以推测Combiner只能应该用于那种Reduce的输入key/Value与输出key/Value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。Combiner的使用一定得慎重,如果用好,它对job执行效率有帮助,反之会影响reduce的最终结果。

       

        4 每次溢写,会在磁盘上生成一个溢写文件,如果map的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个溢写文件存在。

        当map Task真正完成时,内存缓冲区的数据也全部溢写到磁盘中形成一个溢写文件。最终磁盘中会至少有一个这样的溢写文件存在(如果map的输出结果很少,当map执行完成时,只会产生一个溢写文件),因为最终的文件只有一个,所以需要将这些溢写文件归并到一起,这个过程就叫做Merge

        Merge是怎么样的?

        如前边的例子,Reduce Task端将“aaa”从某个map Task端读取过来时值为5,从另外一个Map Task端读取的值为8,因为他们有相同的的key,所以他们mergegroup

        什么是group?

        对于“aaa”就是这样的:{“aaa,[5,8,2…],数组中的值就是从不同的溢写文件中读取过来的,然后再把这些值加起来。

        请注意,因为Merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,那么Mapper也会使用Combiner来合并相同的key

以上这个merge应该是reduce端的mergemapmerge的应该是同一个map Task的溢写文件。

        至此,Map端的所有工作都已结束,最终生成的这个文件也存放在Task Tracker(即Map Task)可以抓取的某个本地目录内。

每个reduce Task不断通过RPCjobTracker那里获取map Task是否完成的信息,如果reduce Task得到通知,获知TaskTracker上的map Task执行完成,Shuffle的后半段过程开始启动。

        简单说,reduce Task在执行之前的工作就是不断地拉取当前job里的每个map Task的最终结果,然后对从不同地方拉取拉取过来的数据不断地做Merge,也最终形成一个文件作为reduce Task的输入文件。

 

        如map端的细节图,Shufflereduce端的过程也能用图上标明的三点来概括。

        当前reduce copy数据的前提是它要从JobTracker获得有哪些map Task已执行结束。

        Reducer真正运行之前,所有的时间都是在那拉取数据,做Merge,且不断重复地在做。

以下分段描述reduce端的Shuffle细节:

        1  Copy过程,简单地拉取数据。

        Reduce进程启动一些数据copy线程(Fecher),通过Http方式请求map Task所在的TaskTracker获取map Task的输出文件。因为map Task早已结束,这些文件就贵TaskTracker管理在本地磁盘中。

 

 

 

        2 Merge阶段。 

        这里的Mergemap端的merge动作,只是数组中存放的是不同mapcopy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端灵活,它基于JVMHeap Size设置,因为Shffler阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:

                               1)内存到内存 2)内存到磁盘 3 磁盘到磁盘

        默认情况下第一种形式不启用,当内存中的数据量达到一定阀值,就启动内存到磁盘的merge。与map端类似,这也是溢写过程,这个过程如果你设置有Combiner,也是会启动的,然后再磁盘中生成了众多的溢写文件。第二种Merge方式一致在运行,知道没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。

 

        3  Reducer的输入文件

不断地Merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对于我们来说当然是希望它放在内存中,直接作为Reducer的输入,但是默认情况下,这个文件是存放在磁盘的。当reducer的输入文件已定,整个Shuffle才最终结束。然后就是reducer

执行,把结果放在HDFS上。

 

 

以上内容摘录自:http://langyu.iteye.com/blog/992916便于理解。

 

步骤4:reduce

整个MapReduce(map)任务执行过程中,默认reduce任务只有一个,来计算洗完牌的数据。

四个步骤的数据是按顺序执行。

 

Mapper

Map-Reduce的思想就是“分而治之”

  1. Mapper负责“分”,即把复杂的任务分解为若干个“简单的任务”执行

“简单的任务”有几个含义:

  • 数据或计算规模相对于原任务要大大缩小
  • 就近计算,即会被分配到存放了所需数据的节点进行计算。
  • 这些小任务可以并行计算,彼此间几乎没有依赖关系。

 

 

Hadoop计算框架reducer

  • 对map阶段的结果进行汇总
  • Reducer的书目由mapred-site.xml配置文件里的项目mapred.reduce.tasks决定。默认值1,用户可以覆盖之。

 

Hadoop计算框架Shuffler

  • 在mapper和reducer中间的一个步骤
  • 可以把mapper的输出按照某种key值重新切分和组合成n份,把key值符合某种范围的输出送到特定的reducer那里去处理
  • 可以简化reducer过程

 

 

 

  • 总结

洗牌分为两个阶段:map Task阶段和reduce Task阶段,这两个阶段任务都是task Tracker任务跟踪器

map Task阶段的洗牌

---第一阶段的洗牌

当map Task输出之后,所有内存中的数据都会经过partition和sort,但是不是所有数据都要会Spill To Disk,因为最后一次输出的数据可能内存没有满,不需要溢写到磁盘。

数据(key/value)溢写前的计算:

  •  Partition分区号计算,数据被分到哪一个reduce Task,可以自定义Partition算法,默认是key的hashcode值对reduce个数取模,默认reduce是1。
  • Sort排序计算:对Map Task输出的数据排序,默认算法对key的ascii码值排序即字段排序,其实是比较算法,比较ASCII码值。
  • Spill To Disk,溢写到磁盘临时文件,随着map Task的不停输出,内存数据越来越多,于是磁盘上会生成一个又一个的临时文件,为了节省磁盘空间,小文件将merge on disk合并成一个大文件,但是数据没有改变。

 

 

reduce Task阶段的洗牌

reduce Task执行前会进行第二阶段的洗牌:

        首先要做的是Copy或者Phase,就是将map Task端第一个阶段的洗牌后的数据抓取到reduce端来,数据当然也可能被其他的reduce端抓取过去,抓取的规则就是数据的分区号,而且不单单从第一个map Task端去抓取,也可能从其他的map Task端去抓取。注意数据不会被多个reduce Task端抓取,只会是取模后的reduce分区。

        抓取过来的数据优先放置在内存中Memory Buffer,这个过程也有溢写spill To Disk,溢写之前也要进行Sort排序。排序后再进行分组。分组后得到分组号,每一组数据传给reduce Task。

        抓取程序是计算框架自带的网络拷贝程序,将数据取到reduce Task端后会再次再次进行排序。因为reduce Task端所抓取的数据可能来自多个map Task端,那么数据顺序整体肯定就是乱的,所以再次比较key的ASCII码值,跟map Task端的排序算法是一样的。

        排完还要进行分组,默认分组算法是根据key是否相等来分组,key相等则在同一个组。分完组,每一组的数据传给reduce Task,由key的排序知道,传给reduce Task的顺序也是确定的。传给reduce Task就进行计算,整个Shuffle就结束了!

 

操作出现的次数

  • 分组:在reduce端
  • 分区Partition:在map端
  • 排序Sort:在map和reduce端都有
  • 合并:文件的合并叫Merge,内存中数据的合并叫Combiner, 默认情况下,没有combiner合并.如果有,在Combiner之前需要做的是找出单词相同的,就是分组。也就是说如果设置了Combiner,分组会执行两次,一次是map Task输出的时候,一次是在reduce Task之前。

 

MapReduce的Split大小

Max.split(100M) 最大碎片大小

Min.split(10M)  最小碎片大小

Block(64M)  hadoop 1.0是64M,2.0是128M

Max(min.split,min(max.split,block) 是计算框架切割数据碎片的算法

根据这个公式,上边的split计算结果是64M,告诉我们即任何一个碎片大小不能超过一个block。就是说只能在一个block里切。

 

如果block大小是128M,根据上边公式计算结果split 碎片是100M,一个碎片大小不能超过100M,那么一个block将会被切成两个,一个100,一个28M

猜你喜欢

转载自blog.csdn.net/shengqianfeng/article/details/82797935