前言
海量数据处理是目前许多程序员面临的一个难题。尽管我们的计算机硬件在飞速的发展,但是相对于互联网中日益增长的数据来说,计算机的处理能力就相形见绌。处理海量数据可以从算法方面入手。同样的也存在一些常用的处理海量数据的编程模型。例如hadoop的mapReduce编程模型。接下来的篇章我们就从这一个编程模型的架构来了解hadoop是如何处理海量数据。
概念:
计算机的分布式:简单的说就是把一个庞大的任务交给n多个计算机去处理。其中mapReduce就是一种简单的分布式计算模型。mapReduce的功能:运用n多个计算机处理同一堆海量的数据得到最终的结果。
mapReduce主要包含两个步骤:一是map,另外一个就是reduce。map中文翻译为映射,即将一组输入映射为一组或者多组全新的数据,而不去改变原始的数据,reduce的功能就是将map得到的数值经过某种方法归一成输出值
举一个例子:单词计数的例子。假设有3篇文章
paper1: we study algorithm
paper2: we share our thinking
paper3:this team shares thinking of the algorithm
根据mapReduce的模型,将这3篇文章交给3个不同的处理器处理:
map操作:(其中一个处理器的map操作情况)
之后对中间结果进行分区处理,将相近的word映射到同一个临时文件中,最后进行汇总,即reduce的操作。这样对3篇文章的词频统计就结束了。
mapReduce编程模型的深入理解:
对于mapReduce:用户首先创建一个map函数处理一个基于key/value的数据集合,输出中间的基于key/value的数据集合,然后创建一个reduce函数来合并具有相同中间值key的中间值value。mapReduce架构的程序是能够在大量的普通的计算机上实现并行化处理。这个系统在运行的时候值关心:如果切分输入的数据,在大量计算机组成集群上如何进行调度,集群中计算机故障的处理,以及管理集群计算机之间必要的通信。
mapReduce编程模型:
mapReduce编程模型的原理:利用一个输入key/value集合产生一个中间的输出key/value的集合,对于mapReduce库的用户上述过程由库中的map来实现。用户自定义的map函数接受一个输入的key/value值,然后产生一个中间key/value值。mapReduce的库把所有具有相同中间key值和中间value值组合在一起传递给reduce函数。
那么用户自定义的reduce函数接受一个中间key值和相关的value值集合。reduce函数合并这些value值形成一个较小的value值的集合。一般的每次的reduce调用只产生一个或者零个的输出value值。
使用mapReduce模型编程的一些例子:
分布式的Grep:map 函数输出匹配某个模式的一行,reduce函数是一个恒等函数,即把中间数据复制到输出。
计算URL的访问频率:map函数处理日志中web页面请求的记录,然后输出中间值(URL,1)。Reduce函数把相同的URL的value值累加起来,产生(URL,记录总数)结果
倒转网络连接图:map函数在源页面source中搜索所有连接到目标target并输出(target,source)。reduce把相同的target连接组合成一个列表,输出
每个主机的检索词向量:检索词向量(词、频率)列表来表示出现在文档中的最重要的一些词。map函数为每一个输入文档输出(主机名、检索词向量)reduce函数接收特定主机的所有文档的检索词向量,并把这些检索词向量加在一起,去掉低频的检索词。最后输出(主机名,检索词向量)
mapReduce模型的实现:
mapReduce模型可以有很多种不同的实现方式。具体取决于具体的应用环境。
下面给出一种Google内部广泛使用的运算环境实现:
1,x86架构,linux操作系统,双处理器,2-4G内存的机器
2,普通的网络硬件设备
3,成千上百的机器(机器故障是常态)
4,代价的IDE硬盘为存储介质。文件系统通过数据复制备份来实现和保证数据的可靠性和有效性
mapReduce的执行概况:
通过调用mapReduce的库函数将输入的数据自动的分割成M个数据片段的集合,map调用被分布到多台机器上执行,输入的数据片段能够在不同的机器上并行处理。使用分区函数将map调用产生的中间key值分成R个不同的分区(分区函数默认为hash函数),reduce调用也被分配到多台机器上执行。分区数量和分区函数由用户指定。
1,用户程序首先调用mapReduce库将输入的文件分成M个数据片,每个数据片段大小一般在16M到64M(这个大小可以通过参数指定)。然后用户程序在机器集群中创建大量的程序副本。
2,这些程序副本中有一个特殊的程序master,其他的程序是worker程序,由master分配任务,master将一个map或者reduce的任务分配给一个空闲的worker
3,被分配了map任务的worker程序读取相关的输入数据片段,从输入数据片段中解析出key/value,然后把key/value传递给用户自定义的map函数,由map输出中间的key/value并且缓存在内存中。
4,缓存中的key/value通过分区函数分成R个区域,之后周期性的写入到本地磁盘上,缓存的key/value在本地的存储位置返回给master,master负责将这些存储的位置信息再次传送给Reduce worker
5,当Reduce worker接收master传送过来的中间值key/value的存储位置时,将这些中间位置从磁盘中读取这些数据,通过按照key值进行排序,之后把相同的key值的value组合在一起。这个排序是必要的,如果中间数据太大无法进行内部排序,就调用外部排序算法。
6,Reduce worker程序遍历排序后的中间数据,对于每个唯一的中间key,Reduce worker把这个key和相关的value值,传递给用户自定义的Reduce函数。最后输出
7,当所有的map和Reduce任务都完成后,master唤醒用户程序。
master数据结构:
master持有一些数据结构,它存储每一个map和Reduce任务的状态(空闲,进行,完成)以及worker的标识。另外master又像一个数据管道,中间数据存储的位置信息通过master传递给Reduce worker。因此对于每个已经完成的map任务,master必须存储map产生的中间文件的位置大小等信息
mapReduce模型的容错机制:
worker故障:master会周期性的ping每个worker。在一个约定的时间范围内没有收到worker的返回信息,master标记这个worker已经失效。所以由这个worker执行的map任务被重新设定为空闲,之后这些任务会安排给其他的worker。同样worker失效时正在运行的map或者Reduce任务也被重新置为空闲状态,等待重新调用
当worker故障时,由于已经完成的map任务的输出放置在该机器闪,位置变成不可访问,需要重新执行。
当一个map任务首先被分配个给work A执行,之后worker A失效又被调度到worker B执行,会通知所有还没从worker A取数据的Reduce改从worker B取数据
master失效:一个程序中只有一个master,master失效的话处理过程比较复杂,一个简单的解决方法是让master周期性的将上面描述的数据结构写入到磁盘。一旦这个master任务失效,可以从最后一个检测点开始启动另一个master进程。不过一般我们再程序运行过程中如果master失效,就停止该程序的运行,重新执行
存储位置:
在计算机运行环境中,网络带宽是一个相当匮乏的资源,我们通过尽量把输入数据存储在本地磁盘啦节省网络带宽。GFS把每个文件按照64MB一个block分割,每个block保存到多台计算机,就是在整个集成环境中存放多份拷贝(一般是3份)。mapReduce的master在调度map任务时候会考虑输入的文件位置信息,尽量把一个map任务调度到在包含相关输入数据拷贝的机器上执行。如果上述努力失败了,master将试着将map任务分配到存放待输入数据的机器的邻居机器上执行。
任务粒度:
任务粒度指的是上述我们把map拆分成M个片段,把Reduce拆分成R个片段执行。理想的情况下M和R的值应该比集群中worker的数量要多得多。这样每个worker都执行大量不同的任务能够提高集群的动态负载和均衡能力
备用任务:
影响一个mapReduce的总的执行时间最通常的因素“落后者”:在运算的过程中,如果有那么一台机器花了很长的时间擦完成最后几个map和Reduce,这样就会使整个mapReduce的执行超过预定的时间。产生“落后者”的原因很多:例如硬盘故障,网络传输速率低等,解决这个问题,就引入了备用任务Bworker。如果出现落后者,就让Bworker同时取执行“落后者”的任务,如果任务执行结束,则整个mapReduce的执行过程结束。
mapReduce的扩展功能:
分区函数:mapReduce的使用者通常会指定Reduce任务和Reduce任务输出的文件数量(R)。我们对中间值使用分区函数进行分区,之后再输入到后续的任务当中执行。一个默认的分区函数一般为hash函数,另外mapReduce库中hi提供一些额外的分区函数,供专门使用
顺序保证:中间值key/value的处理是按照key的增量顺序处理。这样就可以保证每个Reduce输出一个有序的文件
combiner函数:在某些情况下map函数产生的中间key值的重复数据会占用很大的比重。并且用户自定义的Reduce满足结合和交换的定律。例如统计词频的map函数,产生的中间数据相同的(string ,1),这些所有的数据将通过网络发送到同一个Reduce进行处理。这时候允许我们再本地选用一个combiner函数,在本地将相同的数据进行一次合并,然后将合并的结果传递出去
输入和输出的类型:mapReduce库支持几种不同的格式的数据的输入。比如。文本模式:输入的每一行被视为一个key/value.key是文件的偏移量,value是那行的内容。
跳过损坏的记录。mapReduce的调试,一般采用mapReduce库中的本地实现版本。
mapReduce的性能: