目录
1、MapReduce的基本概念
1.1基本概念
- Mapreduce是一个分布式运算程序的编程框架,主要用于hadoop的数据分析应用,是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群;
- 程序往往需要分成至少2个阶段,第一个阶段的maptask和第二个阶段的reduce task,完全并行运行,互不相干,但是第二阶段的数据是依赖第一个阶段的数据;
- MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个mapreduce程序,串行运行;
1.2序列化机制
- Java的序列化比较重(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,header,继承体系等),不便于在网络中高效传输。
- 所以,hadoop自己开发了一套序列化机制(Writable),用户的自定义bean只需要重写该接口的write和readFileds方法。
-
常用的数据类型对应的hadoop数据序列化类型:
1.3Map Task 和 ReduceTask数量的设置
- maptask的数量取决于切分文件的大小以及数量,每一个切片分配一个maptask来处理,。
- reducetask数量并不是任意设置,要考虑业务逻辑需求,有些情况下,需要计算全局汇总结果,就只能有1个reducetask。
- reducetask的数量不能少于分区的数量,否则部分分区的数据都不能被处理。
- 如果分区数不是1,但是reducetask为1,是否执行分区过程。答案是:不执行分区过程。因为在maptask的源码中,执行分区的前提是先判断reduceNum个数是否大于1。不大于1肯定不执行。
- 扩展:mapreduce的切片机制;
- 提交任务之后,会对获取到的文件集合进行遍历,每个文件都会进行分片;
- 默认情况,切片大小=blocksize块大小128MB,每次切片都会判断剩下的部分是否需要大于块的1.1倍,比如129M就不会进行切片;
1.4大量小文件优化策略
- 默认情况下对任务的切片是按文件来规划切片的,不管文件多小,都会是一个单独的切片,都会交给一个maptask,这样会浪费空间和资源;
- 最好的办法,在数据处理系统的最前端(预处理/采集),将小文件先合并成大文件,再上传到HDFS做后续分析。
- 补救措施:如果已经是大量小文件在HDFS中了,可以使用CombineTextInputFormat来做切片,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个maptask去处理。
-
//如果文件大小在2-4m之间就会做一次归并,依次类推 job.setInputFormatClass(CombineTextInputFormat.class) CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);// 4m CombineTextInputFormat.setMinInputSplitSize(job, 2097152);// 2m
1.5排序机制
- 排序是MapReduce框架中最重要的操作之一。Map Task和Reduce Task均会对数据(按照key)进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。
- 对于Map Task,它会将处理的结果暂时放到一个缓冲区中,当缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次排序,并将这些有序数据通过分区写到磁盘上,而当数据处理完毕后,在它会对磁盘上同一分区的文件进行一次合并,以将同一分区的小文件合并成一个大的有序文件。
- 对于Reduce Task,它从每个Map Task上远程拷贝对应分区的数据文件,如果文件大小超过一定阈值--内存大小,则放到磁盘上,否则放到内存中。如果磁盘上文件数目达到一定阈值,则进行一次合并以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据写到磁盘上。当所有数据拷贝完毕后,Reduce Task统一对内存和磁盘上的所有数据进行一次合并。
- 自定义bean对象排序需要实现WritableComparable接口重写compareTo方法;
-
@Override public int compareTo(FlowBean o) { // 倒序排列,从大到小 return this.sumFlow > o.getSumFlow() ? -1 : 1; }
1.6Combiner合并机制
- combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量。
- combiner是在分区和排序之后生成溢出文件之前进行的。
-
public class WordcountCombiner extends Reducer<Text, IntWritable, Text, IntWritable>{ @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int count = 0; for(IntWritable v :values){ count = v.get(); } context.write(key, new IntWritable(count)); } } job.setCombinerClass(WordcountCombiner.class);
2、MapReduce的工作流程
2.1Map阶段的步骤
- Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
- Collect阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
- Spill阶段:即“溢写”,当环形缓冲区满达到阈值后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行Combiner合并、压缩等操作。
- 溢写阶段详情:
- 步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照key进行排序,然后按照分区编号partition进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
- 步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner会在分区和排序之后写到溢出文件之前执行。
- 步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当期内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。
- 当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。
2.2Shuffle阶段的步骤
- maptask收集我们的map()方法输出的kv对,放到内存缓冲区中
- 从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件,多个有序的溢出文件会被合并成一个大的有序溢出文件
- 在溢出过程中,要调用partitoner进行分组和针对key进行排序,如果用户设置了Combiner会在分区和排序之后写到溢出文件之前执行。
- reducetask根据自己的分区号,去各个maptask机器上取相应的结果分区数据
- reducetask会取到同一个分区的来自不同maptask的结果文件,reducetask会将这些有序文件再进行merge合并(归并排序)
- merge合并有三种形式:1)内存到内存 2)内存到磁盘 3)磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。
- 不断地merge,最后生成一个“最终文件”后,shuffle的过程也就结束了,后面进入reducetask的逻辑运算过程(从文件中取出一个一个的键值对group,调用用户自定义的reduce()方法)
- 注意:
- 缓冲区的大小可以通过参数调整,参数:io.sort.mb 默认100M
- Reducer的输入文件,这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。
2.3Reduce阶段的步骤
- Copy阶段:ReduceTask从各个MapTask上rpc远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,溢写到磁盘上,否则直接放到内存中。
- Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行merge合并,以防止内存使用过多或磁盘上文件过多,无论如何最终都会生成一个reduce的输入文件。
- Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可在分区内有序,相同的key进行合并即可。
- Reduce阶段:reduce()函数将计算结果写到HDFS上。
优秀博客:https://www.iteye.com/blog/langyu-992916
3、MapReduce与Yarn
3.1 Yarn概述
- Yarn是一个资源调度平台,负责为运算程序提供服务器运算资源
- Yarn只提供运算资源的调度(用户程序向Yarn申请资源,Yarn就负责分配资源)
- Yarn中的主管角色叫ResourceManager,具体提供运算资源的角色叫NodeManager
- Yarn是一个通用的资源调度平台,企业中各种运算集群都可以整合在Yarn上去执行
- YARN的基本思想:将 MR的JobTracker 两个主要的功能分离成单独的组件,一个全局的ResourceManager和每个应用对应的ApplicationMaster,这样就可以达到计算应用和资源调度解耦合;
- 比如:MapReduce和Spark就都可以运行在yarn上;
3.2Yarn工作机制
- Mr程序提交到客户端所在的节点
- yarnrunner向Resourcemanager申请一个application。
- RM(Resourcemanager)将该应用程序的资源路径返回给yarnrunner
- 该程序将运行所需资源提交到HDFS上
- 程序资源提交完毕后,申请运行mrAppMaster
- RM将用户的请求初始化成一个task
- 其中一个NodeManager领取到task任务。
- 该NodeManager创建容器Container,并产生MRAppmaster
- Container从HDFS上拷贝资源到本地,进行文件分割等等
- MRAppmaster向RM 申请运行maptask容器
- RM将运行maptask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器。
- MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动maptask来执行。
- MRAppmaster向RM申请2个容器,运行reduce task。
- reduce task向maptask获取相应分区的数据。
- 程序运行完毕后,MR会向RM注销自己。
5、基于数据仓库的Hive
5.1数据仓库
- 汇聚了来自各个维度的数据(日志的,数据库的,爬虫的....),主要用于数据的查询,不可更新如果进行了修改会进行数据的备份。
5.2Hive基本概念
- 专门对大数据进行离线的分析使用的工具适用于数据分析,特征处理等任务,它的底层是把HQL转化为MapReduce程序,并且数据存储在HDFS上,程序运行在yarn上。(经常是深夜的定时任务,处理完后自动存放入数据库中。)
- ps:所以然,Hive主要是基于数据的查询而不是更新,否则你的数据仓库由于数据备份的问题,会变得很庞大。
5.3Hive的优缺点
- 使用类SQL语言代替了MapReduce的编写,编写方便,处理大数据比一般的数据库有优势;
- 由于是机器生成的代码,所以性能上会有差异。
5.4Hive的架构
- 基于用户编写的类SQL语句通过Parser(解析器)进行解析--类似词法分析,语法分析;
- 在通过Physical Plan(编译器)翻译成MapReduce程序,Query Optimizer(优化器)在进行语句的优化,最终Execution(执行器)执行任务到Hadoop上。