大数据------MapReduce

1.MapReduce思想:

简单理解就是“天下大事分久必合,合久必分”,MapReduce就完美的体现“分”与“和”的思想。

Map负责“分”,把复杂的任务分解成多个简单的任务,之后进行并行处理的动作。但是前提是:这个复杂的大任务能够进行拆分,并且拆分之后各个子任务之间没有依赖关系。

Reduce负责“和”,就是把map阶段各个小任务的处理结果汇总。

2.MapReduce计算的实现思路:

    2.1. 传统的计算方式:编写java程序,然后获取数据并且拉取过来进行计算(MapReduce计算方式因为要处理数据比较大,用这种办法不可行)。

    2.2. Map阶段计算方式:简单理解就就是HDFS已经对原文件进行分布式存储的情况下,我们的编写java程序到之前存储的各个数据块去计算处理,得到我们想要的数据。

    2.3. Reduce阶段计算方式:编写java程序把Map计算的各个数据结果进行汇总。

3. MapReduce设计构思:

       MapReduce是一个分布式计算框架,核心是将用户自己编写的计算业务逻辑程序和自带默认组件整合一个完整的分布式运算程序,并发运行在hadoop集群上。简单理解就是我们需要做的是编写计算程序代码,之后就交给MapReduce就可以了。MapReduce使开发难度大大的降低了。

MapReduce 处理的数据类型是<key,value>键值对。

key : 这一行的起始点在文件中的偏移量

value: 这一行的内容

 

4. MapReduce框架结构:

在MapReduce分布式运行时有三个进程:

    4.1. MapTask:负责 map 阶段的整个数据处理流程

    4.2. ReduceTask:负责 reduce 阶段的整个数据处理流程

    4.3. MRAppMaster:负责整个程序的过程调度及状态协调

5.  MapReduce  编程规范:

    5.1:用户编写的计算程序包括三个部分:Mapper,Reducer,Driver(提交运行 mr 程序的客户端)

    5.2:mapper的输入与输出数据都是以KV对的形式(KV可自己定义,符合要求即可)在读取数据的时候会默认调用inputformat,是一行一行的读取待处理的数据每一行都对应了一对KV。

    5.3:mapper的业务逻辑计算在map()方法中,并且对每一个KV调用一次。

    5.4:reducer的输入类型必须是mapper的输出类型,也是KV,它的业务逻辑在reduce()中,并且对每一个KV调用一次。

    5.5: 用户自定义的 Mapper 和 Reducer 都要继承各自的父类。

    5.6: 整个程序需要交给一个Drvier来提交,提交的是一个描述了各种必要信息的 job 对象。

6. MapReduce 示例编写:

    6.1:创建一个maven项目导入相关依赖。

    6.2:定义一个mapper类(举例)

//首先要定义四个泛型的类型

//keyin: LongWritable  valuein: Text

Keyin、valuein表示mapper阶段输入KV类型

//keyout: Text valueout:IntWritable

Keyout、valueout表示mapper阶段输出KV类型

public class 类名 extends Mapper<LongWritable, Text, Text, IntWritable>{

//map 方法的生命周期: 框架每传一行数据就被调用一次

//key : 这一行的起始点在文件中的偏移量

//value: 这一行的内容

@Override

protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException

{

//这里就是开发人员自己写的计算逻辑代码

 

context.write(K,V);

 

}

}

    6.3. 定义一个 reducer (举例)

//生命周期:框架每传递进来一个 kv 组,reduce 方法被调用一次

@Override

protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException,InterruptedException {

这里需要开发人员编写reduce阶段处理计算数据的相关逻辑代码

context.write(K,V);

}

}

    6.4。定义一个主类,用来描述 job  并提交 job(举例)

public class Runner {

//把业务逻辑相关的信息(哪个是 mapper,哪个是 reducer,要处理的数据在哪里,输出的结果放哪里……)描述成一个 job 对象

//把这个描述好的 job 提交给集群去运行

public static void main(String[] args) throws Exception {

Configuration conf = new Configuration();

Job wcjob = Job.getInstance(conf);

//指定我这个 job 所在的 jar 包

//  wcjob.setJar("该jar包在hadoop上的路径");

wcjob.setJarByClass(Runner.class);

wcjob.setMapperClass(Mapper.class);

wcjob.setReducerClass(Reducer类名.class);

//设置我们的业务逻辑 Mapper 类的输出 key 和 value 的数据类型

wcjob.setMapOutputKeyClass(Text.class);

wcjob.setMapOutputValueClass(IntWritable.class);

//设置我们的业务逻辑 Reducer 类的输出 key 和 value 的数据类型

wcjob.setOutputKeyClass(Text.class);

wcjob.setOutputValueClass(IntWritable.class);

//指定要处理的数据所在的位置

FileInputFormat.setInputPaths(wcjob, "hdfs存储路径格式例如(hdfs://hdp-server01:9000/wordcount/data/big.txt)");

//指定处理完成之后的结果所保存的位置

FileOutputFormat.setOutputPath(wcjob, new Path("hdfs存储路径格式例如(hdfs://hdp-server01:9000/wordcount/output/)"));

//向 yarn 集群提交这个 job

boolean res = wcjob.waitForCompletion(true);

System.exit(res?0:1);

}

 

7.Keyin、valuein、Keyout、valueout的数据类型:

首先有一些特殊,普通数据类型(比如String、Long)是jdk自带类型,在序列化的时候用这些类型效率低下,因此我们hadoop对这些普通类型进行了封装,它们的对应关系如下:

Long-------LongWritable

String-----Text

Integer----IntWritable

Null-------NullWritable

其中Keyin这一行的起始点在文件中的偏移量所以是固定的类型LongWritable。  valuein、Keyout、valueout自定义即可(但是注意因为map的输出是reduce的输入所以一般情况下valuein、Keyout类型相同)。

8.MapReduce的程序运行模式:

    8.1本地运行:

(1)mapreduce 程序是被提交给 LocalJobRunner 在本地以单进程的形式运行

(2)而处理的数据及输出结果可以在本地文件系统,也可以在 hdfs 上

(3)怎样实现本地运行?写一个程序,不要带集群的配置文件

本质是程序的 conf 中是否有 mapreduce.framework.name=local 以及

yarn.resourcemanager.hostname 参数

本地模式非常便于进行业务逻辑的 debug,只要在 eclipse 中打断点即可

    8.2 集群运行:

(1将 mapreduce 程序提交给 yarn 集群,分发到很多的节点上并发执

(2)处理的数据和输出结果应该位于 hdfs 文件系统

(3)提交集群的实现步骤:

将程序打成 JAR 包,然后在集群的任意一个节点上用 hadoop 命令启动,启动命令语句例如:

hadoop jar jin.jar jin.yu.sheng.mrsimple.JinDriver args

9.MapReduce处理流程解析:

上面我们简述了一个mr程序编程与运行实现的简单案列。但是他的底层是怎样实现的呢?逻辑原理又是什么呢?

    9.1map过程任务执行详解:

        9.1.1把目录文件按照标准进行逻辑切片,形成切片规划。默认情况下,Split size = Block size,一个切片由一个maptask处理。

        9.1.2 把切片中的数据解析成看k,v键值对,默认是把每一行数据解析成一个键值对,key 是每一行的起始位置(单位是字节),value 是本行的文本内容。

         9.1.3 调用mapper中的map方法,每一个键值对都会调用一次map方法。

        9.1.4 照一定的规则对第三阶段输出的键值对进行分区。默认是只有一个区。分区的数量就是 Reducer 任务运行的数量。默认只有一个Reducer 任务。

        9.1.5 分区中的键值对进行排序,首先,按照键进行排序,对于键相同的键值对,按照值进行排序。

       9.1.6 如果有需要,对数据进行局部聚合处理,也就是 combiner 处理。键相等的键值对会调用一次 reduce 方法。经过这一阶段,数据量会减少。 本阶段默认是没有的。

    9.2 reduce过程任务执行详解:

        9.2.1  Reducer 任务会主动从 Mapper 任务复制其输出的键值对。Mapper 任务可能会有很多,因此 Reducer 会复制多个 Mapper 的输出。

        9.2.2 复制到 Reducer 本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。

        9.2.3 对排序后的键值对调用 reduce 方法。键相等的键值对调一次reduce 方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到 HDFS 文件中。

 

 

在整个MapReduce程序的开发过程中,我们最大的工作量是覆盖map函数和覆盖reduce函数。

 

10.MapReduce的序列化:

序列化(Serialization):把结构化数据转换成字节流。

反序列化(Deserialization):把字节流转化成结构化对象。

当要在进程间传递对象或持久化对象的时候,就需要序列化对象成字节流,反之当要将接收到或从磁盘读取的字节流转换为对象,就要进行反序列化,hadoop 自己开发了一套序列化机制( Writable),精简,高效。

Writable是Hadoop的序列化格式,hadoop定义了这样一个Writable接口。一个类要支持可序列化只需实现这个接口即可。

 public interface Writable {

void write(DataOutput out) throws IOException;

void readFields(DataInput in) throws IOException;

 }

11 reduce分区partition:

Reducetask的个数决定了我们输出文件的个数,我们也可以自定义分区,让具有相同性质的数据到同一个分区里面。

自己编写一个类继承partitioner类,在getpartition方法里面返回的就是实际的分区编号,我们重新定义分区的规则,就可以实现自定义分区,自定义分区个数要满足与reducetask个数相等,

12 MapReduce的combiner

在map阶段计算完成之后,如果重复数据太多,太大,可能会影响reduce执行效率,所以出现了位于它们之间的中间聚合组件--combiner,它是优化MapReduce的一种手段,它的父类是reduce,但是它的数量与maptask相等,在每一个,maptask后面都会对应一个combiner,combiner 的意义就是对每一个 maptask 的输出进行局部汇总,以减小网络传输量,combiner 能够应用的前提是不能影响最终的业务逻辑,而且,combiner 的输出 kv 应该跟 reducer 的输入 kv 类型要对应起来

13 maptask的工作机制

MapReduce开发,在map阶段会调用一个inputformat来读取数据,inputformat会见文件切分split,切分之后形成一个一个的数据块(每块大小128M),在进行数据计算的时候,每一个数据块都会对应一个maptask,所以我们maptask的数量多少取决于我们要操作计算数据的大小,

然后map阶段开始处理数据,并且将结果交给outputcollectro收集器,如果我们分区有多个(reducetask的个数有多个),那么在此时就已经分区,然后交给内存环形缓冲区,当内存环形缓冲区快要满的时候(默认大小100M,当达到80%=80M的时候会写入磁盘)溢出(spill)写入数据到磁盘,这是磁盘上的还是临时文件,这些临时文件还进行了soft字典排序,所以每一个分区文件都是分区且排序的,当整个maptask结束后,再将这些临时文件合并(merge)。

14 reducetask工作机制:

启动reducetask的个数在我们运行mr程序的时候就定义好了,定义多少个就有多少个,对应的也拥有相同个分区。每一个reducetask会去对应的maptask端拉取属于自己分区的数据,这个过程叫fecther线程,将数据拉取到内存中,之后进行合并,但是注意合并的操作不在内存中,在磁盘中,然后进行reduce的计算操作,并将结果输出,输出用outputformat类将最后结果数据存储到hdfs集群。

15 shuffle机制

简单来说就是数据如何从map阶段传递给reduce阶段叫做shuffle,不是一个组件只是一个过程。

    15.1 collect阶段:将maptask的结果输出到100M大小的环形缓冲区。

    15.2 spill阶段:将环形缓冲区的文件达到阀值的时候,需要写入(溢出)磁盘,在写入磁盘之前需要对数据进行一次排序的操作。

    15.3 merge阶段:对溢出文件进行合并。

    15.4 copy阶段:ReduceTask 启动 Fetcher 线程到已经完成 MapTask 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。

    15.5 merge阶段:在reducetask赋值数据的同时,在后台会开启多线程对内存带本地的数据文件进行合并操作。

    15.6 sort阶段:在对数据进行合并的同时,会进行排序操作,由于 MapTask

阶段已经对数据进行了局部的排序,ReduceTask 只需保证 Copy 的数据的最终整体有效性即可。

Shuffle 中的缓冲区大小会影响到 mapreduce 程序的执行效率,原则上说,

缓冲区越大,磁盘 io 的次数越少,执行速度就越快(默认大小100M)。

16 MapReduce并行度机制

    16.1 maptask的并行度机制:

是指在map阶段有多少个maptask共同处理任务,maptask个数的多少会影响到我们整个任务(job)的处理速度

map  阶段并行度由客户端在提交 b job  时决定,即客户端提交 job 之前会对待处理数据进行 逻辑切片(默认大小128M)。切片完成会形成切片规划文件(job.split)),每个逻辑切片最终对应启动一个maptask。

逻辑切片机制由 FileInputFormat 实现类的  getSplits()方法完成。

在 FileInputFormat 中,计算切片大小的逻辑:

Math.max(minSize, Math.min(maxSize, blockSize));

切片主要由这几个值来运算决定:

minsize:默认值:1

配置参数: mapreduce.input.fileinputformat.split.minsize

maxsize:默认值:Long.MAXValue

配置参数:mapreduce.input.fileinputformat.split.maxsize

blocksize

默认情况下,split size =block size,在hadoop 2.x中为128M。

minSize与maxSize的大小可以由开发者设定,但是我们maptask的机制是逐个遍历(比如文件1大小:10k,文件2大小:1k,也会产生2个maptask来执行),所以不论怎么调参数,都不能让多个小文件“划入”一个split。

针对小文件:最好的处理方式是在上传hdfs的时候进行小文件的合并。

对于下一个maptask的产生会始终计算bytesRemaining/splitSize,当 bytesRemaining/splitSize > 1.1 不满足的话,那么最后所有剩余的会作为一个切片。从而不会形成例如129M文件规划成两个切片的局面。

    16.2 reducetask的并行度机制

是指在reduce阶段会产生多少个reducetask来执行,与 maptask的并发数由切片数决定不同,Reducetask 数量的决定是可以直接手动设置(默认为1):

如果设置了多个reducetask,就会产生对应的结果文件,就需要考虑每一个文件中的数据量是否分布均匀,可能会造成数据的倾斜。同时还要考虑我们分区规则是否符合要求,如果不符合,要自定义分区组件。

如果有全局的要求,那么最终输出的结果文件一定只能是一个,也就是只能有一个reducetask。

     16.3 task并行度优化

无论是map还是reduce,task的运行时间至少为一分钟,每一个 task(map|reduce)的 setup 和加入到调度器中进行调度,这个可能会花费几秒钟,如果每一个task都快速的跑完,那么在每一个task的开始于结束都浪费的大量时间,此外每一个task都是一个JVM事例,开启与销毁都需要开销,解决的办法配置task的JVM重用,

我们前面讲过文件上传hdfs会对文件进行切块(block)默认hdfs的一个block与我们map阶段的一个切片大小相等,如果我们input的文件非常大,可以在hdfs对block大小进行配置,增大block(默认128M)为256M或者521M。

17 MapReduce的优化

我们上面说到的大部分的属性值与环境都是可以配置的改变的,比如一个task运行占据的内存大小、占用的CPU核数、环形缓冲区的大小默认配置、task最大重试次数、task的失败比例、task的超时时间、

18 MapReduce的其他功能

    18.1 计数器

计数器(Counter)为我们提供一个窗口,用于观察 MapReduce Job 运行期的各种细节数据。对MapReduce 性能调优很有帮助,MapReduce 性能优化的评估大部分都是基于这些Counter 的数值表现出来的。

内置计数器包括:

文件系统计数器(File System Counters)

作业计数器(Job Counters)

MapReduce 框架计数器(Map-Reduce Framework)

Shuffle 错误计数器(Shuffle Errors)

文件输入格式计数器(File Output Format Counters)

文件输出格式计数器(File Input Format Counters)

比较常用的是用来进行全局统计,这时在map阶段就已经对操作的细节数据进行全局统计了,正常需要在reduce输出是才会实现(reducetask的数量必须为1的前提下)。

    18.2 多job的串联

实质多个MapReduce串联完成数据计算的操作,多 job 的串联可以借助 mapreduce 框架的 JobControl 实现,但是在现在的开发中随着任务式调度框架(azkaban、oozie)的出现,这一种方式已经不用了。任务式调度框架会单独发布一篇博客来说明。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值