MapReduce详解

Mapreduce的主要思想: 分而治之
主要分为两个步骤: map和Reduce
map(映射):对所有数据分割后的每一个单独模块分别进行处理
reduce:对map处理后的结果进行合并。
一个基本的MapReduce程序:input -> map -> reduce -> output===其它的并行编程中的种种复杂问题,如分布式存储、工作调度、负载平衡等都由yarn实现。
并行编程模型的基本处理过程如下:
首先Map节点对所划分的数据进行并行处理,从不同的输入数据产生相应的中间结果输出;
然后各个Reduce节点各自进行计算,各自负责处理不同的中间结果数据集合;
再注意在进行Reduce之前,必须要等到所有的Map任务执行完成,且在进入Reduce之前会有一个过程将Map的输出结果进行汇总(shuffle),以便Reduce节点可以完全基于本节点上的数据计算最终的结果;
汇总所有Reduce的输出结果即可获得最终结果。
在MapReduce中会涉及到一些比较常用的数据类型:
数据类型都实现了Writable接口,以便使用这些类型定义的数据可以被序列化进行网络传输和文件存储。
自定义数据类型的时候,需要实现Writable接口!
MapReduce的具体执行流程
首先MapReduce提供了一个InputFormat对象用来读取数据,并进行切分,切分后的数据被切分成为多个InputSplit,每一个InputSplit将对应一个map任务。注意InputSplit存储的并不是数据本身,二是一个分片长度和一个记录数据位置的数组。
在进入Map之前,需要通过InputFormat方法调用getRecordReader()方法生成RecordReader,RecordReader在通过createKey()、createValue()方法创建可供map处理的<key,value>对; RecordReader即为从数据分块中读取数据记录并转化为键值对的类!
在Map输出中间结果之前, 需要经过一个Conbiner对象将该map输出的相同主键key下的所有键值对合并成为一个键值对。
之后map会将输出的中间结果发送给Reduce节点,在这之前,还是需要通过Partitioner对象进行数据分区,将数据发送到合适的Reduce节点上,避免不同Reduce节点上的数据有相关性、,保证每一个Reduce节点可以独立完成本地计算。并且在传入Reduce节点之前还会自动将所有键值对按照主键值进行排序;
最后,Reduce几点计算完成之后,会经过OutputFormat对象指定输出数据的具体格式,最终将数据输出并写回到HDFS上。
Input详解:
首先MapReduce提供了一个InputFormat对象用来读取数据,并进行切分,切分后的数据被切分成为多个InputSplit,每一个InputSplit将对应一个map任务。注意InputSplit存储的并不是数据本身,二是一个分片长度和一个记录数据位置的数组。
在进入Map之前,需要通过InputFormat方法调用getRecordReader()方法生成RecordReader,RecordReader在通过createKey()、createValue()方法创建可供map处理的<key,value>对; RecordReader即为从数据分块中读取数据记录并转化为键值对的类!
Mapper详解:
Mapper类里面主要包含了一下几个方法(按照执行次序):
首先是setup方法:该方法用于Mapper类实例化时应用程序可能需要做的一些初始化工作(比如说创建一个全局的数据结构、进行数据库连接等)
然后是map方法:该方法主要承担了数据处理工作,将输入的key-value对处理计算后转为相应格式的输出!
最后则是cleanup方法:最后的收尾工作执行者。负责关闭文件、关闭数据库连接、或者分发map后的键值对等
Shuffle详解:
shuffle一共分为两个部分:分别是mapper端shuffle和reduce端shuffle
首先Mapper端shuffle:
在Map端的shuffle过程是对Map的结果进行分区、排序、分割,然后将属于同一划分(分区)的输出合并在一起并写在磁盘上,最终得到一个 分区有序 的文件

    我们按照图中的1234步逐步进行说明: 
在map端首先接触的是InputSplit,在InputSplit中含有DataNode中的数据,每一个InputSplit都会分配一个Mapper任务。 
当key/value被写入缓冲区之前,都会被 序列化为字节流。mapreduce提供Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reduce task处理(分区)。默认对key hash后再以reduce task数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。
注意: 虽然Partitioner接口会计算出一个值来决定某个输出会交给哪个reduce去处理,但是在缓冲区中并不会实现物理上的分区,而是将结果加载key-value后面。物理上的分区实在磁盘上进行的。
每个map有一个环形内存缓冲区,用于存储任务的输出。默认大小100MB(io.sort.mb属性)。 
一旦达到阀值80%(io.sort.spil l.percent),一个后台线程就把内容写到(spill:溢写)Linux本地磁盘中的指定目录(mapred.local.dir)下的新建的一个溢出写文件。在这一步会执行两个操作排序和Combiner(前提是设置了Combiner)。
这里大家可能会出现疑问: 是将哪部分溢写到磁盘上那?答案是,溢写线程启动时,会锁定这80M的内存,执行溢写过程。而剩余的那20M缓冲区会继续接收map的输出,直到缓冲区写满,Map 才会被阻塞直到spill 完成。spill操作和接收map输出的操作是两个独立的线程,故互不影响。
spill 线程在把缓冲区的数据写到磁盘前,会进行partition、sort和combine等操作。 会对它进行一个二次快速排序,首先根据数据所属的partition (分区)排序,然后每个partition 中再按Key 排序。输出包括一个索引文件和数据文件。如果设定了Combiner,将在排序输出的基础上运行。 Combiner 就是一个简单Reducer操作,它在执行Map 任务的节点本身运行,先对Map 的输出做一次简单Reduce,使得Map 的输出更紧凑,更少的数据会被写入磁盘和传送到Reducer,节省了网络传输的带宽 。spill 文件保存在由mapred.local.dir指定的目录中,map 任务结束后删除。
每次溢写会在磁盘上生成一个溢写文件,如果map的输出结果很大,有多次这样的溢写发生,磁盘上相应的就会有多个溢写文件存在。而如果map的输出很小以至于最终也没有到达阀值,那最后会将其缓冲区的内容写入磁盘。 
因为最终的文件只有一个,所以需要将这些溢写文件归并到一起 , 
这个过程就叫做Merge。因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,也会使用Combiner来合并相同的key。
从这里我们可以得出,溢写操作是写到了磁盘上,并不一定就是最终的结果,因为最终结果是要只有一个文件,除非其map的输出很小以至于没有没有发生过溢写(也就是说磁盘上只有一个文件)。
到这里,map端的shuffle就全部完成了。
此外,在map端的shuffle过程中,还可以使用压缩,的方式将map的输出结果进行压缩,以减小网络开销。压缩与解压缩使用的是CompressionCode的实现来实现。通过设置configuration即可实现!
然后Reduce端shuffle:
在Reduce端,shuffle主要分为复制Map输出、排序合并两个阶段。
Reduce Task会到map task运行的机器上,拷贝要处理的数据!
map完成后,会通过心跳将信息传给tasktracker,其进而通知jobtracker,reduce task不断地通过RPC从JobTracker那里获取map task是否完成的信息, 当得知某个TaskTracker上的map task执行完成,Reduce端的shuffle就开始工作了
注意: 这里是reduce端的shuffle开始工作,而不是reduce操作开始执行,在shuffle阶段reduce不会运行。
同样我们按照图中的标号,分为三个阶段进行讲解。 
**①** Copy阶段 :reduce端默认有5个数据复制线程从map端复制数据,其通过Http方式得到Map对应分区的输出文件。reduce端并不是等map端执行完后将结果传来,而是直接去map端去Copy输出文件。 
**②** Merge阶段 :reduce端的shuffle也有一个环形缓冲区,它的大小要比map端的灵活(由JVM的heapsize设置),由Copy阶段获得的数据,会存放的这个缓冲区中,同样, 缓存占用到达一定阈值后会将数据写到磁盘中,同样会进行partition、combine、排序等过程 ,这个过程会一直执行直到所有的map输出都被复制过来,如果形成了多个磁盘文件还会进行合并,最后一次合并的结果作为reduce的输入而不是写入到磁盘中。 
当Reducer的输入文件确定后,整个Shuffle操作才最终结束。之后就是Reducer的执行了,最后Reducer会把结果存到HDFS上。
Reduce详解:
每一个key都会对reduce任务数求hashcode余,得到的值就是reduce任务编号。
整个Shuffle操作才最终结束。之后就是Reducer的执行了
Reduce在执行时候的输入其实就是mapper阶段的输出数据格式。
同样的reduce阶段也存在有setup和cleanup方法,用于初始化操作以及执行收尾工作。此外reduce阶段多了一个reduce()方法用于处理数据。
Output详解:
最后Reducer会把结果存到HDFS上
与input相类似,最终的输出使用的是OutPutFormat下的子类FileOutputFormat进行输出控制。最终的输出是每一个<key,value>对,输出到一行,key与value之间使用\t的分隔符进行分隔,默认的调用了key和value的toString方法!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值