hadoop--之mapreduce框架流程

来源:互联网 发布:下载mp4连续剧软件 编辑:程序博客网 时间:2024/05/17 17:17
hadoop1.x时候的计算框架
    hadoop1.x Mapreduce框架成员介绍

l1)Client
Ø用户编写的MapReduce程序通过Client提交到JobTracker端;同时,用户可通过Client提供的一些接口查看作业的运行状态。在Hadoop内部用作业Job)表示MapReduce程序。一个MapReduce程序可对应若干个作业,而每个作业会被分解成若干个Map/Reduce任务(Task)。
l2)JobTracker
ØJobTracke负责资源监控和作业调度。JobTracker 监控所有TaskTracker 与job的健康状况,一旦发现失败,就将相应的任务转移到其他节点;同时,JobTracker 会跟踪任务的执行进度、资源使用量等信息,并将这些信息告诉任务调度器,而调度器会在资源出现空闲时,选择合适的任务使用这些资源。在Hadoop 中,任务调度器是一个可插拔的模块,用户可以根据自己的需要设计相应的调度器。
l3)TaskTracker
ØTaskTracker 会周期性地通过Heartbeat 将本节点上资源的使用情况和任务的运行进度汇报给JobTracker,同时接收JobTracker 发送过来的命令并执行相应的操作(如启动新任务、杀死任务等)。TaskTracker 使用“slot”等量划分本节点上的资源量。“slot”代表计算资源(CPU、内存等)。一个Task 获取到一个slot 后才有机会运行,而Hadoop 调度器的作用就是将各个TaskTracker 上的空闲slot 分配给Task 使用。slot 分为Map slot 和Reduce slot 两种,分别供MapTask 和Reduce Task 使用。TaskTracker 通过slot 数目(可配置参数)限定Task 的并发度。
l4)Task
ØTask 分为Map Task 和Reduce Task 两种,均由TaskTracker 启动。HDFS 以固定大小的block 为基本单位存储数据,而对于MapReduce 而言,其处理单位是split。split 是一个逻辑概念,它只包含一些元数据信息,比如数据起始位置、数据长度、数据所在节点等。它的划分方法完全由用户自己决定。但需要注意的是,split 的多少决定了Map Task 的数目,因为每个split 只会交给一个Map Task 处理。
    hadoop1.x Mapreduce框架过程详解

        1,run job 
           在客户端,用户编写Java程序,编写完成后,打包为jar包,然后提交。JobClient的run job()方法是用于新建JobClient实例,并调用其submitjob()方法的快捷方式 也可以用 job.waitcomplication()。提交作业后,run job()每秒轮询作业的进度,如果发现自上次报告后有改变,便把进度打印到控制台,作业完成后,如果成功,就显示作业计数器,如果失败,导致错误的原因打印到控制台。
        2,get new job id
            -->向jobTracker请求一个job id (JobTracker.getNewJobId());
            -->检查作业的输出说明,如果输出目录已经存在或者没有指定输出目录,则抛出异常给客户端,
            -->检查作业的输入切片,如果输入分片不能计算如没有指定输入目录,如抛出异常给客户端。
        3,copy job resources
            将运行作业所需要的资源包括
                -->打包好的jar包(运行程序)
                -->配置文件(xml)
                -->计算所得的输入分片
            复制到一job id 命名的目录下jobtracker的文件系统中,作业jar的副本较多(由mapred.submin.replication)控制默认为10
            因此在运行作业的时候,集群中有许多tasktracker访问
        4,summit job
            通知JobTracker 作业准备执行。(通过调用jobtracker的submitjob()方法实现)
        5,initlialize job (初始化)
            当JobTracker接收到对其submitjob()方法的调用后,会把此调用放入一个内部队列中,交由作业调度器(job scheduler)进行调
            并对其进行初始化。初始化包括创建一个表示正在运行的作业对象--封装任务和记录信息,以便跟踪任务的状态和进程。    
         6,retrieve input splits (检索 恢复)
             --> 为了创建任务运行列表,作业调度器首先从共享文件系统中获取JobClient以计算好的输入分片信息,然后为每个分片分配一个                        map 任 务
            --> 创建的reduce任务数量有job的mapred.reduce.task属性决定(setNumReduceTask()设置),scheduler创建相应数量的r                            educe任 务,任务在此时被分配ID
            --> 除了map任务和reduce任务,还有setupJob,cleanupJob需要建立,有tasktracker在所有map开始前和所有reduce结束后分别                        执行,这两个方法在OutPutCommitter中(默认是FileOutputCommiter),setupjob()创建输出目录和任务的临时工作目录,                          cleanupjob删除临时工作目录
        7,heartbeat(returns task)
                -->TaskTracker运行一个简单的循环来定期发送心跳给JobTasker."心跳"告知JobTracker,tasktacker是否还存活,同时也充当着两                    者 之间的消息通道,作为心跳的一部分,tasktracker 还会指明他自己是否准备好运行下一次任务,如果是,jobtracker会为他分                    配一 个新的任务,并通过心跳的返回值与tasktracker进行通信
                -->每个tasktracker会有固定的map和reduce任务槽,数量由tasktracker核的数量和内存的大小来决定,jobtracker会先将                                tasktracker的所有map槽填满,然后再填reduce槽
                -->jobtracker分配map任务时,会选取与输入分片最近的tasktracker,分配reduce任务用不着考虑数据本地化。
        8,retrieve job resources
                -->通过从共享文件系统把作业的JAR文件复制到tasktracker所在的文件系统,从而实现作业的jar文件本地化,同时 tasktracker将                    应用程序所需要的全部文件从分布式缓存复制到本地磁盘。
                -->tasktracker为任务新建一个本地工作目录,并把jar文件中的内容解压到这个文件夹下
        9,launch(发起)
                Tasktracker新建一个TaskRunner实例来运行该任务
        10,run
                TaskRunner启动一个新的JVM来运行每个任务,以便客户的map/reduce不会影响到tasktracker
            
   hadoop1.x Mapreduce框架原理    

        1.5 进度和状态的更新

        一个作业和每个任务都有一个状态信息,包括:作业或任务的运行状态(running, successful, failed),map和reduce的进度,计数器               值,状态消息或描述。

         这些信息通过一定的时间间隔由child JVM –> task tracker –> job tracker汇聚。job tracker将产生一个表明所有运行作业及其任务状态            的全局试图。你可以通过Web UI查看。同时JobClient通过每秒查询jobtracker来获得最新状态。

        Child JVM有独立的进程每隔三秒检查任务更新标志,如果有更新就报告给tasktracker

        tasktracker每隔5秒给jobtracker发心跳

        job tracker合并这些更新,产生一个表明所有运行作业及其状态的的全局视图

        job client 通过每秒轮询Jobtracker来接收最新消息

1.6 作业的完成

 

1.7 作业的失败

 

2. 作业的调度

默认调度器 – 基于队列的FIFO调度器

公平调度器(Fair Scheduler)- 每个用户都有自己的作业池,用map和reduce的任务槽数来定制作业池的最小容量,也可以设置每个池的权重。Fair Scheduler支持抢占,如果一个池在特定的一段时间内未得到公平的资源共享,它会中止运行池得到过多资源的任务,以便把任务槽让给运行资源不足的池。启动步骤:

1) 拷贝contrib/fairscheduler下的jar复制到lib下;

2) mapred.jobtracker.taskScheduler = org.apache.hadoop.mapred.FairScheduler

3) 重启节点hadoop

能力调度器(Capacity Scheduler)-

4. 任务的执行

Hadoop发现一个任务运行比预期慢的时候,它会尽量检测,并启动另一个相同的任务作为备份,即“推测执行”(speculative execution)。

推测执行是一种优化措施,并不能使作业运行更可靠。默认启用,但可以单独为map/reduce任务设置,mapred.map.tasks.speculative.execution和mapred.reduce.tasks.speculative.execution。开启此功能会减少整个吞吐量,在集群中倾向于关闭此选项,而让用户根据个别作业需要开启该功能。

Hadoop为每个任务启动一个新JVM需要耗时1秒,对于大量超短任务如果重用JVM会提升性能。当启用JVM重用后,JVM不会同时运行多个任务,而是顺序执行。tasktracker可以一次启动多个JVM然后同时运行,接着重用这些JVM。控制任务重用JVM的属性是mapred.job.reuse.jvm.num.tasks,它指定给定作业每个JVM运行的任务的最大数,默认为1,即无重用;-1表示无限制即该作业的所有的任务都是有一个JVM。

在map/reduce程序中,可以通过某些环境属性(Configuration)得知作业和任务的信息。

mapred.job.id              作业ID,如job_201104121233_0001

mapred.tip.id               任务ID,如task_201104121233_0001_m_000003

mapred.task.id             任务尝试ID,如attempt_201104121233_0001_m_000003_0

mapred.task.partition    作业中任务的ID,如3

mapred.task.is.map       此任务是否为map任务,如true

 hadoop1.x Mapreduce类型和格式 

2. 输入格式

2.1输入分片与记录

一个输入分片(split)是由单个map处理的输入块(分片个数即map所需的tasktracker个数),每个分片包含若干记录(key+value),map函数依次处理每条记录。输入分片表示为InputSplit接口,其包含一个以字节为单位的长度和一组存储位置,分片不包含数据本身,而是指向数据的引用。

InputSplit是由InputFormat创建的,一般无需应用开发人员处理。InputFormat负责产生输入分片并将它们分割成记录。

1) JobClient调用InputFormat.getSplites()方法,传入预期的map任务数(只是一个参考值);

2)InputFormat计算好分片数后,客户端将它们发送到jobtracker,jobtracker便使用其存储位置信息来调度map任务从而在tasktracker上处理这些分片数据。

3)在tasktracker上,map任务把输入分片传给InputFormat的getRecordReader()方法来获得这个分片的RecordReader;RecordReader基本上就是记录上的迭代器,map任务用一个RecordReader来生成记录的键值对,然后在传给map函数。

2.2 FileInputFormat

输入路径可由多个函数FileInputFormat.addInputPath()指定,还可以利用FileInputFormat.setInputPathFilter()设置过滤器。输入分片的大小有上个属性控制:分片最小字节数,分片最大字节数和HDFS数据块字节数。

mapred.min.split.size, mapred.max.split.size, dfs.block.size

计算公式是:

max(minSplitSize, min(maxSplitSize, blockSize))

没有特殊需求,应该尽量让分片大小和数据块大小一致。如果HDFS中存在大批量的小文件,则需要使用CombineFileInputFormat将多个文件打包到一个分片中,以便mapper可以处理更多的数据。一个可以减少大量小文件的方法(适合于小文件在本地文件系统,在上传至HDFS之前将它们合并成大文件)是使用SequenceFile将小文件合并成一个或多个大文件,可以将文件名作为键,文件内容作为值。

有时候不希望输入文件被切分,只需覆盖InputFormat的isSplitable()方法返回false即可。

有时候map程序想知道正在处理的分片信息,可以通过Configuration中的属性得到,包括map.input.file(正在处理的输入文件的路径),map.input.start(分片开始处的字节偏移量), map.input.length(分片的字节长度)。

有时候map想访问一个文件的所有内容,需要一个RecordReader来读取文件内容作为record的值。可行的方法是实现一个FileInputFormat的子类,将文件标记为不可切分,同时指定一个特定的RecordReader;该RecordReader只是在第一次next()时返回文件的内容。

2.3 文本输入

TextInputFormat是默认的InputFormat。每条记录是一行输入。键是LongWritable类型,存储该行在整个文件中的字节偏移量;值是这行的内容,不包括任何行终止符(换行符和回车符),Text类型。由于一行的长度不定,所以极易出现split分片会跨越HDFS的数据块。

KeyValueTextInputFormat将文件的每一行看作一个键值对,使用某个分界符进行分隔,比如制表符。Hadoop默认输出的TextOutputFormat格式即键值对为一行组成一个文件,处理这类文件就可以使用键值文本输入格式。

NLineInputFormat可以保证map收到固定行数的输入分片,键是文件中行的字节偏移量,值是行内容。默认为1,即一行为一个分片,送给每个map。

2.4 二进制输入

SequenceFileInputFormat存储二进制的键值对的序列。顺序文件SequenceFile是可分割的,也支持压缩,很符合MapReduce数据的格式。

2.5 多种输入

Hadoop也支持在一个作业中对不同的数据集进行连接(join),即定义多个不同的数据输入源,每个源对应不同的目录、输入格式和Map函数。

MultipleInputs.addInputpath(conf, inputPath, TextInputFormat.class, MaxTemperatureMapper.class);

2.6 数据库输入和输出

DBInputFormat用于使用JDBC从关系数据库中读取数据,但只适合少量的数据集。如果需要与来自HDFS的大数据集连接,要使用MultipleInputs。

在关系数据库和HDFS之间移动数据的另一个方法是Sqoop

HBase和HDFS之间移动数据使用TableInputFormat和TableOutputFormat。

3. 输出格式

TextOutputFormat是默认的输出格式,它把每条记录写为文本行,键和值可以是任意类型。

SequenceFileOutputFormat将输出写入一个顺序文件,是二进制格式。MapFileOutputFormat把MapFile作为输出,键必须顺序添加,所以必须确保reducer输出的键已经排好序。

FileOutputFormat及其子类产生的文件放在输出目录下,每个reducer一个文件并且文件由分区号命名,如part-00000,part-00001等。有时候需要对文件名进行控制,或让每个reduce输出多个文件,则可使用MultipleOutputFormat和MultipleOutputs类。

MultipleFileOuputFormat可以将数据写到多个文件,关键是如何控制输出文件的命名。它有两个子类:MultipleTextOutputFormat和MultipleSequenceFileOutputFormat。在使用多文件输出时,只需实现它们任何一个的子类,并覆盖generateFileNameForKeyValue()返回输出文件名。

MultipleOutputs类不同的是,可以为不同的输出产生不同的类型。

MultipleOutputs.addMultiNameOutput(conf, “name”, TextOutputFormat.class, KeyClass, valueClass);

新版本Hadoop中上述两个多输出类也合并。

FileOutputFormat的子类会产生输出文件,即使文件是空的。可以使用LazyOutputFormat来去除空文件。

这章主要总结MapReduce的高级特性,包括计数器,数据集的排序和连接。

 hadoop1.x Mapreduce特性 

1. 计数器

计数器是一种收集作业统计信息的有效手段,由于质量控制或应用统计。计数器还可辅助诊断系统故障。

Hadoop为每个作业维护若干内置计数器,以描述该作业的各项指标。计数器由关联任务维护,并定期(3秒)传到tasktracker,再由tasktracker传给jobtracker(5秒,心跳)。一个任务的计数器值每次都是完整传输的,而非增量值。

MapReduce允许用户编写程序定义计数器,一般是由一个Java枚举(enum)类型定义。枚举类型的名称即计数器组名称,枚举类型的字段即计数器名称。计数器在作业实例级别是全局的,MapReduce框架会跨所有的map和reduce来统计这些计数器,并在作业结束时产生一个最终的结果。

enum Temperature {

    MISSING, MAlFORMED

}

context.incrCounter(Temperature.MISSING, 1);

MapReduce同时支持非枚举类型的动态计数器。

context.incrContext(String group, String counter, int amount);

计数器可以通过很多方式获取,Web界面和命令行(hadoop job -counter指令)之外,用户可以用Java API获取计数器的值。

RunningJob job = jobClient.getJob(JobID.forName(id));

Counters counters = job.getCounters();

long missing = counters.getCounter(MaxTemperatue.Temperature.MISSING);

2. 排序

排序是MapReduce的核心技术,尽管应用程序本身不需要对数据排序,但可以使用MapReduce的排序功能来组织数据。默认情况下,MapReduce根据输入记录的键对数据排序。键的排列顺序是由RawComparator控制的,规则如下:

1)若属性mapred.output.key.comparator.class已设置,则使用该类的实例;

2)否则键必须是WritableComparable的子类,并使用针对该键类的已登记的comparator;

3)如果还没有已登记的comparator,则使用RawComparator将字节流反序列化为一个对象,再由WritableComparable的compareTo()方法进行操作。

全排序

如何用Hadoop产生一个键全局排序的文件?(最好的回答是使用Pig或Hive,两者均可使用一条指令进行排序)

大致方法是,想办法创建一系列排好序的文件,而且这些文件直接也是排序的,比方说第一个文件的值都不第二个文件的值小,则简单的拼装这些文件就可以得到全局排序的结果。问题是如何划分这些文件,并把原始文件的值放入这些排序的文件中?可以使用map的partition来将某一范围的键放入对于的reduce,每个reduce的输入可以保证已排序(局部排序),默认直接输出到part-000×,那所有这些输出组合成一个文件就是全局排序的。为了得到合适的范围,需要对所有输入数据进行统计,实际做法是通过抽样,Hadoop提供InputSampler和IntervalSampler。使用抽样函数事先对input数据进行抽样,得到抽样范围,然后将范围写入分布式缓存,供集群上其它任务使用。

DistributedCache.addCacheFile(cacheFile, conf);

DistributedCache.createSymlink(conf);

辅助排序

MapReduce框架在记录达到reducer之前按键对记录排序,但键所对应的值并没有排序。大多情况下不需考虑值在reduce函数中的出现顺序,但是,有时也需要通过对键进行排序和分组等以实现对值的排序。

例子:设计一个MapReduce程序以计算每年最高气温。

1)使用组合键IntPair,将年份和气温都作为键;

2)按照年份来分区和分组,但排序需要按照年份升序和气温降序。

conf.setPartitionerClass();

conf.setOutputKeyComparatorClass();

conf.setOutputValueGroupingComparator();

3 连接

MapReduce能执行大型数据集间的“连接”操作。

Map端连接指在数据到达map函数之前就执行连接操作。为达到此目的,各map的输入数据必须先分区并且以特定方式排序。各个数据集被划分成相同数量的分区,并且均按相同的键(连接键)排序。同一键的所有记录均会放在同一分区之中。

map连接操作可以连接多个作业的输出,只要这些作业的reduce数量相同,键相同,并且输出文件是不可切分的(如小于HDFS块大小,或gzip压缩)。利用org.apache.mapred.join包中的CompositeInputFormat类来运行一个map端连接,其输入源和连接类型(内连接或外连接)可以通过一个连接表达式进行配置。

Reduce连接不要求数据集符合特定结构,因此比Map连接更为常用。但是,由于数据集均经过mapReduce的shuffle过程,所以reduce端连接的效率往往更低一些。基本思路是mapper为各个记录标记源,并且使用连接键作为map输出键,使键相同的记录放在同一个reducer中。

1)可以使用MultipleInputs来解析和标注各个源;

2)先将某一个数据源传输到reduce。举天气数据为例,气象站信息(气象站id和名字)以气象站ID+“0”为组合键,名字为值,但是按照ID来分区和分组;气象站天气情况(气象站id,时间和气温)以气象站ID+“1”为组合键,气温为值,但是按照ID来分区和分组。两组数据经过不同的map之后,具有相同的ID的记录被合并作为一个记录输入reduce程序,值列表中的第一个是气象站名称,其余的记录都是温度信息。reduce程序只需要取出一个值,并将其作为后续每条输出记录的一部分写到输出文件即可。

conf.setPartitionerClass();

conf.setOutputValueGroupingComparator(Textpair.FirstComparator.class);

4 边数据分布(side data)

边数据是作业所需的额外的只读数据,已辅助处理主数据集。面临的挑战是如何让所有的map和reduce都能方便高效地使用边数据。

1)如果仅需向任务传递少量元数据,则可以通过Configuration来设置每个job的属性,则map/reduce可以覆盖configure()方法来获取这些元数据值。如果你设置的值是复杂对象,则需要处理序列化工作。在几百个作业同在一个系统中运行的情况下,这种方法会增多内存开销,而且元数据信息在所有节点都缓存,即使在不需要它的jobtracker和tasktracker上。

2)针对小数据量边数据的常用办法是将在map/reduce数据缓存在内存中,并通过重用JVM使tasktracker上同一个作业的后续任务共享这些数据。

3)分布式缓存 (-files, -archives)

a)启动作业时,使用files或archives传入元数据文件路径,

%hadoop jar job.jar MaxTempratureSample –file input/metadata/stations-fixed-width.txt input/all output

b)当tasktracker获得任务后,首先将jobtracker中的上述文件复制到本地磁盘,具体在${mapred.local.dir}/taskTracker/archive,缓存的容量是有限的,默认10GB,可以通过local.cache.size来设置。

c)在map/reduce程序中,直接读取“stations-fixed-width.txt”文件。同时可以通过JobConf.getLocalCacheFiles()和JobConf.getLocalCacheArchives()来获取本地文件路径的数组。

5 MapReduce类库

Hadoop还提供了一个MapReduce类库,方便完成常用的功能。

ChainMapper, ChainReducer        在一个MapReduce中运行多个mapper或reducer。(M+RM*)

IntSumReducer, LongSumReducer 对各键的所有整数值进行求和操作的reducer

TokenCounterMapper                  输出各单词及其出现的次数

RegexMapper                             检查输入值是否匹配某正则表达式,输出匹配字符串和计数器值


0 0
原创粉丝点击