MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架
MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上
A.MapReduce易于编程
B.良好的扩展性
C.高容错性
D.适合PB级以上海量数据的离线处理
A.不擅长实时计算
B.不擅长流式计算
C.不擅长DAG(有向图)计算
总结:分析WordCount数据流走向深入理解MapReduce核心思想
一个完整的MapReduce程序在分布式运行时有三类实例进程
A.MrAppMaster:负责整个程序的过程调度及状态协调
B.MapTask:负责Map阶段的整个数据处理流程
C.ReduceTask:负责Reduce阶段的整个数据处理流程
采用反编译工具反编译源码,发现WordCount案例有Map类、Reduce类和驱动类。且数据的类型是Hadoop自身封装的序列化类型
Java类型 | Hadoop Writable类型 |
---|---|
Boolean | BooleanWritable |
Byte | ByteWritable |
Int | IntWritable |
Float | FloatWritable |
Long | LongWritable |
Double | DoubleWritable |
String | TextWritable |
Map | MapWritable |
Array | ArrayWritable |
用户编写的程序分成三个部分:Mapper、Reduce和Driver
A.Mapper阶段
· 用户自定义的Mapper要继承自己的父类
· Mapper的输入数据是KV对的形式(KV的类型可自定义)
· Mapper中的业务逻辑写在map()方法中
· Mapper的输出数据是KV对的形式(KV的类型可自定义)
· map()方法(MapTask进程)对每一个
B.Reducer阶段
· 用户自定义的Reducer要继承自己的父类
· Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
· Reducer的业务逻辑写在reduce()方法中
· ReduceTask进程对每一组相同K的
C.Driver阶段
· 相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是封装了MapReduce程序相关运行参数的job对象
序列化就是把内存中的对象转换成字节序列,以便于存储到磁盘(持久化)和网络传输
反序列化就是将接收到的字节序列或磁盘的持久化数据,转换成内存中的对象
一般来说,“活的”对象只生存在内存里,关机断电就没有了;而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机;然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机
Java的序列化时一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息、Header、继承体系等),不便于在网络中搞笑传输;所以,Hadoop自己开发了一套序列化机制(Writable)
A.紧凑:高效使用存储空间
B.快速:读写数据的额外开销小
C.可扩展:随着通信协议的升级而可升级
D.互操作:支持多语言的交互
实现bean对象序列化步骤:
A.必须实现Writable接口
B.反序列化时,需要反射调用空参构造函数,所以必须有空参构造
C.重写序列化方法(write)
D.重写反序列化方法(readFields)
E.注意反序列化的顺序和序列化的顺序完全一致
F.要想把结果显示在文件中,需要重写toString(),可用“\t”分开,翻遍后续使用
G.如果需要将自定义的bean放在key中输出,则还需要实现Comparable接口,因为MapReduce框架中的Shuffle过程要求对key必须能排序
数据块:Block是HDFS物理上把数据分成一块一块。
数据切片:数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储
A.getSplits:1.如何进行切片 2.可不可以切片this.isSplitable(job, path)
for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1D; bytesRemaining -= splitSize) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length - bytesRemaining, splitSize, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
if (bytesRemaining != 0L) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length-bytesRemaining, bytesRemaining, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
B.切片机制
1.简单的按照文件的内容长度进行切片
2.切片大小,默认等于Block大小
3.切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
形式 | 切片方法 | 返回形式 |
---|---|---|
TextInputFormat | 使用得是FileInputFormat切片方法 | LineRecordReader(将文件以kv对的形式返回) |
NLineInputFormat | 自定义了切片方法(按行切片) | LineRecordReader |
CombineTextInputFormat | 自定义了切片方法(按照设置得大小切片) | CombineFileRecordReader |
FixedLengthInputFormat | 使用得是FileInputFormat得切片方法 | FixedLengthRecordReader(返回得都是固定长度得value) |
KeyValueTextInputFormat | 使用得是FileInputFormat得切片方法 | KeyValueLineRecordReader(key是一行中得第一个单词,value是一行中除了第一个单词之后得字符串) |
SequenceFileInputFormat | 使用得是FileInputFormat得切片方法 | SequenceFileRecordReader(二进制得数据) |
A.自定义一个类继承FileInputFormat
B.改写RecordReader,实现一次读取一个完整文件封装为KV
C.在 输出时使用SequenceFileOutputFormat输出合并文件
A.文件得切片
B.将输入文件转换为key value值,输出到mapper
注意事项:
· ReduceTask=0,表示没有Reduce阶段,输出文件个数和Map个数一致
· ReduceTask默认值就是1,所以输出文件个数为一个
· 如果数据分布不均匀,就有可能在Reduce阶段产生数据倾斜
· ReduceTask数量并不是任意设置,还要考虑业务逻辑需求,有些情况下,需要计算全局汇总结果,就只能有1个ReduceTask
· 具体多少个ReduceTask,需要根据集群性能而定
· 如果分区数不是1,但是ReduceTask为1,是否执行分区过程;答案是:不执行分区过程。因为在MapTask的源码中,执行分区的前提是先判断ReduceNum个数是否大于1;不大于1肯定不执行
Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle
分区的数量对应着reducetask的数量,有几个分区就有几个输出文件
A.自定义Partitioner步骤
· 自定义类继承Partitioner,重写getPartition()方法
· 在Job驱动中,设置自定义Partitioner
· 自定义Partition后,要根据自定义Partitioner的逻辑设置相应数量的ReduceTask
B.分区总结
· 如果ReduceTask的数量> getPartition的结果数,则会多产生几个空的输出文件part-r-000xx
· 如果1 · 如果ReduceTask的数量=1,则不管MapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask,最终也就只会产生一个结果文件 part-r-00000 · 分区号必须从零开始,逐一累加 排序是MapReduce框架中最重要的操作之一 MapTask和ReduceTask均会对数据按照key进行排序;该操作属于Hadoop的默认行为; 任何应用程序中的数据均会被排序,而不管逻辑上是否需要 默认排序是按照字典顺序排序,且实现该排序的方法是快速排序 Combiner是MR程序中Mapper和Reducer之外的一种组件,其组件的父类就是Reducer;Combiner和Reducer的区别在于运行的位置,Combiner是在每一个MapTask所在的节点运行、Reducer是接收全局所有Mapper的输出结果;Combiner能够应用的前提是不能影响最终的业务逻辑,而且,Combiner的输出kv应该跟Reducer的输入kv类型要对应起来 简而言之,Combiner的意义就是对每一个MapTask的输出进行局部汇总,以减小网络传输量 对Reduce阶段的数据根据某一个或几个字段进行分组 分组排序步骤: · 自定义类继承WritableComparator · 重写compare()方法 · 创建一个构造将比较对象的类传给父类 OutputFormat接口实现类 OutputFormat是MapReduce输出的基类,所有实现MapReduce输出都实现了OutputFormat接口;常见的OutputFormat实现类:TextOutputFormat(文本输出)、SequenceFileOutputFormat(格式紧凑,很容易被压缩) 自定义OutputFormat(实现控制最终文件的输出路径和输出格式) 步骤:自定义一个类继承FileOutputFormat;改写RecordWrited,具体改写输出数据的方法write() Map端 为来自不同表或文件的key/value对,打标签以区别不同来源的记录,然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出 Reduce端 在Reduce端以连接字段作为key的分组已经完成,只需要在每一个分组当中将那些来源于不同文件的记录(在Map阶段已经打标记)分开,最后进行合并就可以了 Map Join适用于一张表十分小,一张表很大的场景 在Reduce端处理过多的表,非常容易产生数据倾斜,怎么办? 在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜;具体办法:采用DistributedCache Hadoop为每个作业维护若干内置计数器,以描述多项指标 在运行核心业务MapReduce程序之前,往往要先对数据进行清洗,清洗掉不符合用户要求的数据;清洗的过程往往只需要运行Mapper程序,不需要运行Reduce程序 压缩技术能够有效减少底层存储系统(HDFS)读写字节数,压缩提高了网络带宽和磁盘空间的效率。在数据规模很大的工作负载密集的情况下要花大量的时间,因此,使用数据压缩显得非常重要 压缩是提高Hadoop运行效率的一种优化策略 采用压缩技术减少了磁盘IO,但同时增加了CPU运算负担。所以,压缩特性运用得当能提高性能,但运用不当也可能减低性能 压缩原则:运算密集型的job,少用压缩;IO密集型的job,多用压缩 压缩率比较高,而且压缩/解压速度也比较快 应用场景:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用Gzip压缩格式 具有很高的压缩率,比Gzip压缩率都高;压缩/解压速度慢 应用场景:适合对速度要求不高,但需要较高的压缩率的时候;或输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间,并且以后数据用的比较少 的情况;或对单个很大的文本文件想压缩减少存储空间,同时又需要支持切分,而且兼容之前的应用程序的情况下 压缩/解压速度比较快,合理的压缩率(比Gzip要低一些);在应用中对LZO格式的文件需要做一些特殊处理 应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,LZO优点越明显 高速压缩速度和合理的压缩率(比Gzip要低) 应用场景:作为一个MaoReduce作业的输出和另一个MapReduce作业的输入 压缩可以在MapReduce作用的任意阶段启用 Yarn是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台,而MapReduce等运算程序则相当于运行于操作系统之上的应用程序 YARN主要由ResourceManager、NodeManager、ApplicationMaster和Container等组件构成 A. ResourceManager(RM):处理客户端请求;监控NodeManager;启动或监控ApplicationMaster;资源的分配与调度 B. NodeManager(NM):管理单个节点上的资源;处理来自ResourceManager的命令;处理来自ApplicationMaster的命令 C. ApplicationMaster (AM):负责数据的切分;为应用程序申请资源并分配给内部的任务;任务的监控与容错 D. Container:Container是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等 作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业 第2步:Client向RM申请一个作业id 第3步:RM给Client返回该job资源的提交路径和作业id 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径 第5步:Client提交完资源后,向RM申请运行MrAppMaster 作业初始化 第6步:当RM收到Client的请求后,将该job添加到容量调度器中 第7步:某一个空闲的NM领取到该Job 第8步:该NM创建Container,并产生MRAppmaster 第9步:下载Client提交的资源到本地 任务分配 第10步:MrAppMaster向RM申请运行多个MapTask任务资源 第11步:RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器 任务运行 第12步:MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序 第13步:MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask 第14步:ReduceTask向MapTask获取相应分区的数据 第15步:程序运行完毕后,MR会向RM申请注销自己 进度和状态更新 YARN中的任务将其进度和状态(包括counter)返回给应用管理器, 客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应用管理器请求进度更新, 展示给用户 作业完成 除了向应用管理器请求作业进度外, 客户端每5秒都会通过调用waitForCompletion()来检查作业是否完成。时间间隔可以通过mapreduce.client.completion.pollinterval来设置。作业完成之后, 应用管理器和Container会清理工作状态。作业的信息会被作业历史服务器存储以备之后用户核查 目前,Hadoop作业调度器主要有三种:FIFO、Capacity Scheduler和Fair Scheduler。Hadoop2.7.2默认的资源调度器是Capacity Scheduler 支持多队列多用户,每个队列中的资源量可以配置,同一队列中的作业公平共享队列中所有资源 比如有三个队列:queueA、queueB和queueC,每个队列中的job按照优先级分配资源,优先级越高分配的资源越多,但是每个 job 都会分配到资源以确保公平 在资源有限的情况下,每个job理想情况下获得的计算资源与实际获得的计算资源存在一种差距,这个差距就叫做缺额 在同一个队列中,job的资源缺额越大,越先获得资源优先执行。作业是按照缺额的高低来先后执行的,而且可以看到上图有多个作业同时运行 MapReduce 程序效率的瓶颈在于两点: 计算机性能 CPU、内存、磁盘健康、网络 I/O 操作优化 数据倾斜 Map和Reduce数设置不合理 Map运行时间太长,导致Reduce等待过久 小文件过多 大量的不可分块的超大文件 Spill次数过多 Merge次数过多等 MapReduce优化方法主要从六个方面考虑:数据输入、Map阶段、Reduce阶段、IO传输、数据倾斜问题和常用的调优参数 数据倾斜现象 数据频率倾斜——某一个区域的数据量要远远大于其他区域 数据大小倾斜——部分记录的大小远远大于平均值 减少数据倾斜的方法 方法1:抽样和范围分区 可以通过对原始数据进行抽样得到的结果集来预设分区边界值 方法2:自定义分区 基于输出键的背景知识进行自定义分区。例如,如果Map输出键的单词来源于一本书。且其中某几个专业词汇较多。那么就可以自定义分区将这这些专业词汇发送给固定的一部分Reduce实例。而将其他的都发送给剩余的Reduce实例 方法3:Combine 使用Combine可以大量地减小数据倾斜。在可能的情况下,Combine的目的就是聚合并精简数据 方法4:采用Map Join,尽量避免Reduce Join 资源相关参数 以下参数是在用户自己的MR应用程序中配置就可以生效(mapred-default.xml) 应该在YARN启动之前就配置在服务器的配置文件中才能生效(yarn-default.xml) Shuffle性能优化的关键参数,应在YARN启动之前就配置好(mapred-default.xml) 容错相关参数(MapReduce性能优化) HDFS上每个文件都要在NameNode上建立一个索引,这个索引的大小约为150byte,这样当小文件比较多的时候,就会产生很多的索引文件,一方面会大量占用NameNode的内存空间,另一方面就是索引文件过大使得索引速度变慢 小文件的优化无非以下几种方式:3.3.3 WritableComparable排序
排序分类
解析
部分排序
MapReduce根据输入记录的键对数据集排序。保证输出的每个文件内部有序
全排序
最终输出结果只有一个文件,且文件内部有序。实现方式是只设置一个ReduceTask。但该方法在处理大型文件时效率极低,因为一台机器处理所有文件,完全丧失了MapReduce所提供的并行架构
辅助排序
在Reduce端对key进行分组。应用于在接收的key为bean对象时,想让一个或几个字段相同(全部字段比较不相同)的key进入到同一个reduce方法时,可以采用分组排序
二次排序
在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序
3.3.4 Combine合并
3.3.5 GroupingComparator分组
15.6 OutputFormat数据输出
3.7 Join多种应用
3.7.1 Reduce Join工作原理
15.7.2 Map Join工作原理
15.8 计数器应用
3.9 数据清洗(ETL)
第4章 Hadoop数据压缩
4.1 概述
4.2 MR支持的压缩编码
压缩格式
Hadoop是否自带
算法
文件扩展名
是否可切分
换成压缩格式后,原来的程序是否需要修改
DEFLATE
是
DEFLATE
.deflate
否
和文本处理一样,不需要修改
Gzip
是
DEFLATE
.gz
否
和文本处理一样,不需要修改
bzip2
是
bzip2
.bz2
是
和文本处理一样,不需要修改
LZO
否
LZO
.lzo
是
需要建索引,还需要指定输入格式
Snappy
否
Snappy
.snappy
否
和文本处理一样,不需要修改
4.3 压缩方式选择
4.3.1 Gzip压缩
4.3.2 Bzip2压缩
4.3.3 LZO压缩
4.3.4 Snappy压缩
4.4 压缩位置选择
第5章 Yarn资源调度器
5.1 Yarn基本架构
5.2 Yarn工作机制
5.3 作业提交全过程
5.3.1 作业提交过程之YARN
5.3.2 作业提交过程之MapReduce
5.4 资源调度器
5.4.1 先进先出调度器(FIFO)
5.4.2 容量调度器(Capacity Schedule)
5.4.3 公平调度器(Fair Scheduler)
第6章 Hadoop企业优化
6.1 MapReduce跑的慢的原因
6.2 MapReduce优化方法
6.2.1 数据输入
6.2.2 Map阶段
6.2.3 Reduce阶段
6.2.4 I/O传输
6.2.5 数据倾斜问题
6.2.6 常用的调优参数
配置参数
参数说明
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.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
配置参数
参数说明
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 the ApplicationMaster.”。
6.3 HDFS小文件优化方法
6.3.1 HDFS小文件弊端
6.3.2 HDFS小文件解决方案