MapReduce基本原理
MapReduce简介
MapReduce是一个并行计算的框架
–提供并行计算能力,随着节点数增加近似线性递增
–分而治之的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成
–并行编程对程序员透明,降低编程难度,方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。
MapReduce是一种编程模型
–用于大规模数据集(大于1TB)的并行运算。
–每个MapReduce任务都被初始化为一个Job,每个Job又可以分为两种阶段:map阶段和reduce阶段。这两个阶段分别用两个函数表示,即map函数和reduce函数。
MapReduce的组件及架构
作业job
客户端要求执行的一个工作单元。输入数据,MapReduce程序,配置信息.
任务task
MapReduce将作业拆成的小单元。
Map任务和reduce任务。
MapReduce的缺陷
可扩展性
–JobTracker内存中保存用户作业的信息
–JobTracker使用的是粗粒度的锁
可靠性和可用性
–JobTracker失效会丢失集群中所有的运行作业,用户需手动重新提交和恢复工作流
对不同编程模型的支持
–HadoopV1以MapReduce为中心的设计虽然能支持广泛的用例,但是并不适合所有大型计算。
Map和reduce函数
map函数
–接受一个键值对(key-valuepair),产生一组中间键值对,传递给reduce函数。
reduce函数
–接受一个键和一组值,将这组值合并产生一组规模更小的值。
MapReduce处理中,数据的流程是什么
在客户端,JobTracker,TaskTracker的层次来分析MapReduce的工作原理:
A.在客户端启动一个作业。
B.向JobTracker请求一个Job ID。
C.将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件,配置文件和客户端计算所得的输入划分信息。这些信息都存放在JobTracker专门为改作业创建的文件夹中。这些文件都存放在JobTracker专门为该作业创建的文件夹中。文件夹名为该作业的Job ID。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息告诉了JobTracker应该为这个作业启动多少个map任务等信息。
D.JobTracker接收到作业后,将其放在一个作业队列里,等待作业调度器对其进行调度,当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和reduce槽。这里需要强调的是:map任务不是随随便便地分配给某个TaskTracker的,这里有个概念叫:数据本地化(Data-Local)。意思是:将map任务分配给含有该map处理的数据块的TaskTracker上,同时将程序JAR包复制到该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。
E.TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行,同时心跳中还需携带很多信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。
MapReduce详细过程
分片split
分片
n MapReduce把输入的数据划分成等长的小数据块,成为输入分片input split,简称分片。
n 一个split只能由单个map操作来处理。
n 每个分片被划分为若干个记录,每个记录就是一个键/值对,map一个接一个地处理记录。
分片的需要
MapReduce的输入必须是可分割类型
常见文本类型
HDFS特定类型:TextFile,SequenceFile等。
计算数据本地化
在本地存有HDFS数据的节点上运行map任务
Split大小的选择
1. 分片越小,负载越平衡。
异构时根据计算机性能分配任务个数。
失败重启更加平衡。
2. 分片越小,框架开销越大
每个分片一个map任务
管理分片的总时间和构建map任务时间变大
3. 块太大
Map数太小,整体的作业变慢。
4. 默认HDFS块大小,128MB。
避免切分
避免分片的情况
–有些应用程序希望用一个mapper完整处理每一个输入文件
–比如:检查一个文件中所有记录是否有序,最简单的方法是顺序扫描每一条记录并且比较后一条记录是否比前一条要小
第一种方式
–将分片设置成大于要处理的文件大小
–虽然简单但不是最有效的
第二种方式
–使用FileInputFormat子类,重载isSplitable()方法,把返回值设置为false
Map端
Map端的输出并不是直接写入磁盘中,而是利用缓冲的方式写到内存并且处于效率的考虑进行预排序。
每个map任务都有一个环形内存缓冲区用于存储任务输出。
--默认缓冲区大小为100M
--一定缓冲内容达到阈值(转为是80%),一个后台线程会将内容spill到磁盘,生成一个spill文件,任务完成前,spill文件会被合并成一个已分区且已排序的输出文件
--如果在其期间缓冲区填满,map会被阻塞知道写磁盘完成
--溢出的文件会写到由mapred.local.dir属性指定的本地目录中
在写磁盘之前,线程会根据最重要传的reducer把数据划分成相应的partition
--每个分区,后天线程会按键进行内存排序
--如果有combiner,就会在排序后的输出上运行,可以减少写到磁盘的数据和传递给reduer的数据。
Spill过程
Reduce端
复制阶段
---Reducer通过http方式得到所有map输出文件的分区
---每个map任务的完成时间可能不同,以此只要有一个任务完成,reduce任务就开始复制其产生的输出
---获取的map输出相当小,可以复制到reduceJVM的内存中,否则复制到磁盘
reducer如何知道要从哪台机器获取map输出?
map任务完成后,会通知tasktracker,然后由tasktracker通知jobtracker,jobtrakcer会知道map输出和tasktracker之间的关系,reducer的一个线程会定期询问jobtracker获取map的输出以及输出位置。
排序阶段(合并阶段)
复制完所有map输出后,reduce任务进入排序阶段(合并阶段,因为排序在map端已经完成),合并所有map的输出。
合并是循环进行的。比如有50个文件,合并因子是10,则需要进行5次合并操作。
在最后合并时,为了性能,并不写磁盘,而是直接作为数据输入到reduce函数。
Reduce处理阶段
在reduce阶段,对已排序输出中的每个键调用reduce函数。
输出直接写到输出文件系统,一般为HDFS
MapReduce编程模型实例
每个MapReduce的Job分为两种阶段:map阶段和reduce阶段
Hadoop会将mapreduce的输入数据划分为相同大小的split。Hadoop对每个split构建一个map任务。
MapReduce简单模型
MapReduce复杂模型
经典例子
词频统计
–有非常多的文本文件,包含非常多的单词,GB-TB级
–统计每个单词出现的次数
解决方法
–写个小程序,按顺序统计所有单词词频
–写个多线程程序,并发遍历所有文件
–把方法1的程序部署到N台机器上,把文件分成N份,每台机器统计一份文件,最后再统计
–使用MapReduce框架统计
通过map函数和reduce函数统计
Word Count
Shuffle&Partition&Combiner
Shuffle阶段
--系统执行排序的过程(即将map输出作为输入传给reducer)称为shuffle。
--shuffle是MapReduce的核心阶段,是提高MapReduce性能最主要的部分,属于需要不断被优化的部分。
Combiner函数
--shuffle数据过多浪费带宽
--提前在map节点进行部分reduce任务,建设shuffle数据量
Partition函数
–map函数产生的中间键值对映射到多个分区里
–每个分区一部分键,对应的值全部在这个分区中
–每个partition对应一个reduce
–最简单的实现:将键求哈希再对分区数R取值
Collect过程
Kvbuffer:环形字节数组,节省内存空间
•放置<key, value>外还放置一些索引kvmeta
•数据和索引互不重叠,用分界点equator分割,每次spill后,分界点会换位置。数据向上增长,index向下增长
•kvmeta是对<key, value>的索引,是个四元组
–包括value起始位置、key起始位置、partition值、value长度,占用4个int
–一个四元组对应一个kv对
•kvbuffer快满的时候,spill写进磁盘
–80%开始spill,有单独的spill线程执行
Spill过程1
按parition输出,partition内按key升序
–写进spill5.out文件
spill5.out.index三元组记录partition信息
–起始位置、原始数据长度、压缩后的长度
–一个partition对应一个三元组
–还存储crc32校验数据
–不仅可以在磁盘中,也可以存在内存中(默认1MB)
Spill过程2
spill的同时,map继续往kvbuffer写数据
防止bufindex和kvindex碰撞,提前计算分界点
分界点取剩余空间的中间位置
bufindex和kvindex改变位置后继续写入数据
Merge过程
如果map输出数据过多,会有多次spill,多个out和index文件,分布在一个节点的不同磁盘
merge在spill结束之后,kvbuffer占用的内存可以释放
不同out文件中对应相同partition的segment合并
最小化堆多路归并排序,过多可以分多次
Copy过程
Reduce通过http从各个map拖数据
获取的数据内存中能放下就放内存,达到一定程度merge,把merge完的数据写入磁盘
有些map获得的数据过大直接写入磁盘,文件数达到一定阈值开启磁盘merge。
Merge Sort过程
这里的merge与map端的merge类似
map的结果已经有序,reduce端再进行一次归并排序
一般reduce一边copy一边sort,两个阶段重叠