Hadoop - MapReduce学习笔记(详细)
第1章 MapReduce概述
- 定义:是一个分布式运算程序的编程框架
- 优缺点:易于编程、良好的扩展性、高容错性、适合PB级以上数据的离线处理
- 核心思想:MapReduce 编程模型只能包含一个Map 阶段和一个Reduce 阶段
- MapReduce进程:
MrAppMaster,负责整个程序的过程调度及状态协调
MapTask,负责map阶段的数据处理
ReduceTask,负责reduce阶段的数据处理 - 官方WordCount源码:Map 类、Reduce 类、驱动类组成
- 常用数据序列化类型:Hadoop Writable 类型:String Text、array ArrayWritable,其他对应Writable类型,BytesWritable
- MapReduce编码规范:mapper阶段,reducer阶段,driver阶段
- WordCount案例实操:driver类:获取实例和配置信息、三个类的加载、输入输出kv的类型、输入输出路径设置、提交任务
第2章 hadoop序列化
- 把内存中的对象,转化成字节序列,以便存储到磁盘或网络传输。java的序列化太重量级,包含很多其他额外的信息,hadoop序列化,紧凑,快速,可扩展,互操作(多语言)
- 自定义bean对象实现序列化接口:
- 必须实现Writable接口
- 反序列化必须与序列化顺序一致
- 如果需要将自己定义的bean放在key中传输,则还需要实现Compareable接口(shuffle过程要对key排序)
第3章 MapReduce框架原理
InputFormat数据输入
- MapTask并行度由切片数决定;(多少个切片数就有多少个MapTask)
- FileInputFormat抽象类:切片机制:对每个文件单独切片,不考虑整体,文件大于切片大小1.1倍才会切片,默认切片大小等于block大小;切片大小计算公式:Math.max(minSize, Math.min(maxSize)),blockSize))==blockSize==128M,本地计算是32M,集群上是128M,逻辑上对输入进行改变,小于块大小可能会造成磁盘IO。调切片大小调minSize和maxSize
- 其常见实现类:
- TextInputFormat:继承FileInputFormat,框架默认的FileInputFormat实现类,如果有大量小文件,就会产生大量的MapTask,处理效率极其低下。切片机制:按文件切片,KV:按行读取,偏移量(LongWritable),每行的数据(Text)
- CombineTextInputFormat:继承TextInputFormat,切片机制:将多个小文件从逻辑上规划到一个切片中,用于小文件过多的场景,将多个小文件从逻辑上规划到一个切片中,这样多个小文件就可以交给一个MapTask 处理。切片过程:虚拟存储切片最大值设置m - 虚拟存储 - 切片,虚拟存储:小于m的为一块,大于m小于2m的分两块(防止出现太小切),大于2m的分出一个m,剩下的再切分;切片:小于m的块跟其他合并为一个切片。KV:按行读取,偏移量(LongWritable),每行的数据(Text)
- KeyValueTextInputFormat:继承FileInputFormat,切片机制:按文件切片。KV:分隔符分割K(Text)V(Text)
- NLineInputFormat:继承FileInputFormat,切片机制:按n行为一个切片,KV:按n行读取,偏移量(LongWritable),NLine数据(Text)
CombineTextInputFormat,需要在Driver中添加: job.setInputFormatClass(CombineTextInputFormat.class); //默认TextInputFormat.class CombineTextInputFormat.setMaxInputSplitSize(job, 4194304); //虚拟存储切片最大值设置4m 注意:虚拟存储切片最大值设置最好根据实际的小文件大小情况来设置具体的值。 KeyValueTextInputFormat,需要在Driver中添加: conf.set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR, " "); //设置分割符 job.setInputFormatClass(KeyValueTextInputFormat.class); //默认TextInputFormat.class NLineInputFormat,需要在Driver中添加: job.setInputFormatClass(NLineInputFormat.class); NLineInputFormat.setNumLinesPerSplit(job,2); //设置行数
自定义InputFormat:继承FileInputFormat,继承RecordReader
Job提交流程源码
- 创建临时提交文件路径
- 获取切片文件、配置文件、jar包
- 提交资源
- 删除文件路径下的资源
FileInputFormat切片机制:
- 遍历文件;
- 获取文件大小,计算切片大小,判断剩下的是否大于切片的1.1倍,否则就分一块切片。
- 将切片信息写到一个切片规划文件中Job.split
MapReduce工作流程
Shuffle机制
shuffle机制:
1,MapTask收集map方法输出的kv对,放到环形内存缓冲区(默认100M)
2,从缓冲区不断溢出文件,(超过80%溢出或整个切片结束)
3,多个溢出文件会被合并成大的溢出文件
4,在溢出及合并的过程中,会进行分区及排序,(接下来可选:combiner合并分区,压缩,序列化存入磁盘)
5,reducetask会根据自己的分区号,去各个maptask上取结果分区数据
6,reducetask对同一个分区的不同文件再进行合并(归并排序)
7,合并成大文件后shuffle过程也就结束了,后面进行reducetask的逻辑运算(调用用户的reduce())
注意:
缓冲区大小影响mapreduce执行效率,默认100M,参数io.sort.mb
默认分区是key的hashCode对reducetask个数取模得到的,用户没法控制哪个key分到哪个分区(可以自定义Partitioner)
Partition分区
- 将输出进行分类,默认分区是HashPartitioner,根据key的hashCode和ReduceTask个数取模得到的,用户没法控制哪个key存在哪个分区
- 使用:自定义Partitioner类,继承Partitioner,重写getPartitioner()方法。在job驱动中设置分区类,ReduceTask个数(大于1小于分区个数会报IO异常,1个可以,大于分区数也可以)
WritableComparable排序
- 实现这个接口可以让自定义的类型作为key,排序类型:部分排序、全排序、辅助排序、二次排序
- 使用:在作为key的自定义bean中实现WritableComparable接口,重写compareTo()方法定义排序,在map结束之后就会根据key排序,排序方法就是自定义的
Combiner合并
- Driver设置job.setCombinerClass(),可以直接使用reducer的类,或者写Combiner类继承Recuder类
GroupingComparator分组排序
- 对KV进行分组,然后排序输出,比如订单号分组,价格排序,使用bean作为key,定义分组排序的类
- 使用:继承WritableComparator类,重写compare()方法,定义空参构造器调用父类构造器super(beanClass, true)
MapTask工作机制
ReduceTask工作机制
ReduceTask工作机制:
- ---
ReduceTask个数设置:
- 太多太少效率都不高
- ReduceTask=0表示没有reduce阶段,输出文件个数等于MapTask个数,MapTask个数有切片数决定
- ReduceTask默认为1,数量的设置需要根据业务需求、集群性能而定,若计算汇总结果就只能有一个
- 如果Map阶段不同切片数据量分布不均匀,就会出现数据倾斜(不同MapTask任务量差别很大)
- 如果分区数不是1,但是ReduceTask个数不是1,则不执行分区;因为在源码中,分区前会先判断ReduceTask个数是否大于1,不大于1不执行分区
OutputFormat数据输出
- 默认输出是TextOutputFormat,每条记录输出为文本行
- SequenceFileOutputFormat,一般将其输出作为后续MapReduce的输入,格式紧凑,容易被压缩
- 自定义Outputformat,控制最终输出的格式和路径:新建输出类继承FileOutputFormat,新建写记录的类继承RecordWriter类,Driver中指定新建输出类
Join多种应用
- reduce join:Map端为来自不同表或文件的KV对打标签以区别不同的来源记录,然后用连接字段作为K,其他部分和新加的标志作为V,最后进行输出(toString());Reduce端,以连接字段为K的分组已经完成,只需要在每一个分组中将那些来源于不同源(Map端已打标签)的记录分开,最后进行合并
- 缺点:合并的操作在reduce阶段完成,reduce端压力太大,map端运算负载很低,资源利用率不高,reduce阶段极易产生数据倾斜,解决办法:map端实现数据合并
- map join:适用于一张表很小,一张表很大的场景,在Map端提前缓存小表,提前处理业务逻辑,增加Map端业务,减少reduce端压力,尽可能减少数据倾斜,减少Shuffer过程(最耗时间)
计数器应用
- 计数器API:Hadoop为每个job维护若干计数器,以描述多项指标(调试)
- 采用枚举方式统计计数,enum MyCounter{A,B}
对枚举定义的计数器加1:context.getCounter(MyCounter.A).increment(1); - 采用计数器组,计数器名称的方式:context.getCounter("countergroup", "counter").incremene(1).
- 采用枚举方式统计计数,enum MyCounter{A,B}
数据清洗(ETL)
- 在运行核心业务MapReduce 程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper 程序,不需要运行Reduce 程序。使用哦计数器计数清洗的个数
MapReduce开发总结
第4章 hadoop数据压缩
- 概述:提高网络带宽和磁盘空间的效率,可以在任意阶段使用压缩(3个);注意:使用压缩减少了磁盘IO也增加了CPU运算负担;原则:运算密集型job少用压缩,IO密集型多用压缩
- MR支持的压缩编码:deflate,Gzip,bzip2,LZO,Snappy
- 压缩方式的选择:------Map------Reduce-------
- 压缩参数的配置:Map前:core-site.xml设置1个参数,Map后Reduce后mapred-site.xml设置2个参数、3个参数
- 压缩实操案例:数据流的压缩、Map输出端的采用压缩、Reduce输出端采用压缩(Driver中开启压缩,设置压缩格式)
第5章 yarn资源调度器
yarn基本架构:
resourcemanager、nodemanager、container、application master
yarn工作机制
1,MR程序提交到客户端所在的节点
2,客户端的YarnRunner向ResourceManager申请一个application
3,resourcemanager返回资源路径
4,该程序将所需资源提交到hdfs上
5,程序资源提交完毕后,申请运行application master
6,reourcemanager分配一个container并运行application master
7,application master把程序运行所需资源拷贝到本地
8,application master向resourcemanager申请maptask运行的资源
9,resourcemanager分配运行maptask的容器,application master向分配的容器发送启动脚本
10,maptask计算完毕后,application master申请运行reducetask的资源
11,reducetask向maptask节点获取数据,运行reducetask程序
12,程序运行完毕后,mr会向resourcemanager申请注销自己
作业提交过程
资源调度器
- FIFO
- 容量调度(默认),多个任务队列,每个队列分配一定的资源,有可能抢占
- 公平调度,多个任务队列,每个任务队列内的job共享队列的资源,支持抢占,允许调度器终止那些资源超过了公平共享份额的队列的容器(抢占会降低效率)
- 延迟调度,如果一个应用请求一个节点(一般先是请求本地的),由于本地节点可能没有资源,放宽约束到其他节点上运行,实践发现,如果此时等一小段时间,会增加在请求节点上获得容器的机会,等足够多的其他节点的心跳之后再放松本地约束
任务的推测执行
- 前提,每个task只能有一个备份任务,当前完成的task必须不小于5%
- 原理,mr总是选择最低于平均完成时间的任务,以空间换时间;计算每个任务的估计完成时刻t1,计算每个任务的备份任务按照其他已经完成任务的速度执行完成时刻t2,MR总是选择t1-t2最大的任务开启备份任务
第6章 Hadoop企业优化
- MapReduce程序效率瓶颈
- 计算机性能(内存,cpu,磁盘,网络)
- IO操作(数据倾斜,map reduce数设置不合理,小文件过多,大量的不可分的块,溢写次数,合并过多)
- MapReduce优化方法:主要从以下6个方面:数据输入、Map阶段、Reduce阶段、IO传输、数据倾斜问题、常用调优参数
- 数据输入:
- MR运行前合并小文件;大量的小文件会产生大量的Map任务,增大Map装载次数,而任务的装载笔记耗时,从而导致运行缓慢;
- 可以采用CombineTextInputFormat解决输入端大量小文件问题 - Map阶段:
- 减少溢写次数:调整io.sort.mb、sort.spill.percent参数增大出发溢出的上限,减少溢写次数,从而减少磁盘IO
- 减少合并次数:调整io.sort.factor参数,增大合并的文件数目,减少合并的次数,从而缩短MR处理时间
- 在Map之后,在不影响业务逻辑的前提下先进行Combine处理(提前Reduce),减少磁盘IO - Reduce阶段:
- 合理设置Map和Reduce个数:太少会导致Task等待,太多会导致Map、Reduce任务间竞争资源,造成处理超时等错误
- 设置Map和Reduce共存:调整slowstart.completedmaps参数,使用Map运行一定程度后,Reduce也开始运行,减少 Reduce等待时间
- 规避使用Reduce:Reduce在连接数据集时会产生大量的网络消耗
- 合理设置reduce端的buffer:默认情况下,数据达到一个阈值buffer中数据就会溢写到磁盘,然后从磁盘中获取数据。 可以通过调整参数使得一部分数据可以直接输入给Reduce,从而减少磁盘IO:mapreduce.reduce.input.buffer.percent,会保留指定比例的数据在buffer中,直接给Reduce - IO传输:
- 采用数据压缩的方式,减少IO时间,安装Snappy和LZO压缩编码器
- 使用SequenceFile二进制文件 - 数据倾斜问题:
- 抽样和范围分区:可以通过对原始数据进行抽样得到的结果来预设分区边界值
- 自定义分区:基于key进行自定义分区
- Combine:使用Combine可以大量减少数据倾斜,在可能的情况下,Combine的目的就是聚合并精简数据
- 采用Map Join,尽量避免使用Reduce Join - 常用的调优参数
- MR中:mapred-site.xml
- 数据输入:
- mapreduce.map.memory.mb:一个MapTask 可使用的资源上限( 单位:MB),默认为1024。如果MapTask 实际
使用的资源量超过该值,则会被强制杀死。- mapreduce.reduce.memory.mb:一个ReduceTask 可使用的资源上限( 单位:MB),默认为1024。如果ReduceTask
实际使用的资源量超过该值,则会被强制杀死。- mapreduce.map.cpu.vcores:每个MapTask 可使用的最多cpu core 数目,默认值: 1
- mapreduce.reduce.cpu.vcores:每个ReduceTask 可使用的最多cpu core 数目,默认值: 1
- mapreduce.reduce.shuffle.parallelcopies:每个Reduce 去Map 中取数据的并行数。默认值是5
- mapreduce.reduce.shuffle.merge.percent:Buffer 中的数据达到多少比例开始写入磁盘。默认值0.66
- mapreduce.reduce.shuffle.input.buffer.percent:Buffer 大小占Reduce 可用内存的比例。默认值0.7
- mapreduce.reduce.input.buffer.percent:指定多少比例的内存用来存放Buffer 中的数据,默认值是0.0
- Yarn中:yarn-site.xml
- yarn.scheduler.minimum-allocation-mb:给应用程序Container 分配的最小内存,默认值:1024
- yarn.scheduler.maximum-allocation-mb:给应用程序Container 分配的最大内存,默认值:8192
- yarn.scheduler.minimum-allocation-vcores:每个Container 申请的最小CPU 核数,默认值:1
- yarn.scheduler.maximum-allocation-vcores:每个Container 申请的最大CPU 核数,默认值:32
- yarn.nodemanager.resource.memory-mb:给Containers 分配的最大物理内存,默认值:8192
- Shuffle中:mapred-site.xml,Yarn启动之前配置好
- mapreduce.task.io.sort.mb:Shuffle 的环形缓冲区大小,默认100m
- mapreduce.map.sort.spill.percent:环形缓冲区溢出的阈值,默认80%
- 容错相关
- mapreduce.map.maxattempts:每个Map Task 最大重试次数,一旦重试参数超过该值,则认为Map Task 运行失败,默认值:4。
- mapreduce.reduce.maxattempts:每个Reduce Task 最大重试次数,一旦重试参数超过该值,则认为Map Task 运行失败,默认值:4。
- mapreduce.task.timeout:Task 超时时间,经常需要设置的一个参数,该参数表达的意思为:如果一个Task 在一定时间内没有任何进入,即不会读取新的数据,也没有输出数据,则认为该Task 处于Block 状态,可能是卡住了,也许永远会卡住,为了防止因为用户程序永远Block 住不退出,则强制设置了一个该超时时间(单位毫秒),默认是600000。如果你的程序对每条输入数据的处理时间过长(比如会访问数据库,通过网络拉取数据等),建议将该参数调大,该参数过小常出现的错误提示是“ AttemptID:attempt_14267829456721_123456_m_000224_0 Timed out after 300 secsContainer killed by theApplicationMaster.”。
- HDFS小文件优化方法
- 弊端:HDFS 上每个文件都要在NameNode 上建立一个索引,大小约为150byte,这样当小文件比较多的时候,就会产生很多的索引文件,一方面会大量占用NameNode 的内存空间,另一方面就是索引文件过大使得索引速度变慢。
- 解决方案:
- Hadoop Archive,将多个小文件打包成一个har文件
- SequenceFile由一些列二进制kv组成,如果key为文件名,value为内容,则可以将大批小文件合并成一个大文件
- CombineFileInputFormat,一种新的InputFormat,将多个文件合并成一个单独的切片
- 开启JVM重用,对于大量小文件的job,原理:Map在JVM上运行完毕后,会继续运行其他Map,设置map.reduce.job,jvm.numtasks在10-20之间
第7章 MapReduce扩展案例
- 倒排索引案例:有多个文件,每个文件里有多个单词,求每个单词在各个文件中出现的次数
- TopN案例
- 找博客共同好友案例
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/15643712.html