Hadoop三大组件内容
1. Hdfs
1.1. HDFS定义
1.1.1. HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
1.1.2. HDFS的使用场景:
l 适合一次写入,多次读出的场景,且不支持文件的修改。适合用来做数据分析,并不适合用来做网盘应用。
1.2. HDFS的优缺点
1.2.1. 优点:
l 高容错性
- 数据自动保存多个副本。它通过增加副本的形式,提高容错性。
- 某一个副本丢失以后,它可以自动恢复。
l 适合处理大数据
- 数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据;
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大。
l 可构建在廉价机器上,通过多副本机制,提高可靠性。
1.2.2. 缺点:
l 不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
l 无法高效的对大量小文件进行存储。
- 存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的;
- 小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
l 不支持并发写入、文件随机修改。
- 同一时间一个文件只能有一个用户执行写操作,不允许多个线程同时写;
- 仅支持数据append(追加),不支持文件的随机修改。
1.3. HDFS组成架构
1.3.1. NameNode(nn):就是Master,它是一个主管、管理者。
l 管理HDFS的名称空间;
l 配置副本策略;
l 管理数据块(Block)映射信息;
l 处理客户端读写请求。
1.3.2. DataNode:就是Slave。NameNode下达命令,DataNode执行实际的操作。
l 存储实际的数据块;
l 执行数据块的读/写操作。
1.3.3. Client:就是客户端。
l 文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
l 与NameNode交互,获取文件的位置信息;
l 与DataNode交互,读取或者写入数据;
l Client提供一些命令来管理HDFS,比如NameNode格式化;
l Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作;
1.3.4. Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
l 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode ;
l 在紧急情况下,可辅助恢复NameNode。
1.4. HDFS文件块大小(面试重点)
1.4.1. HDFS中的文件在物理上是分块存储(Block),块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在Hadoop2.x版本中是128M,老版本中是64M。
1.4.2. 为什么块的大小不能设置太小,也不能设置太大?
l HDFS的块设置太小,会增加寻址时间,程序一直在找块的开始位置;
l 如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。
l HDFS块的大小设置主要取决于磁盘传输速率。
1.5. HDFS的数据流(面试重点)
1.5.1. HDFS写数据流程
l 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
l NameNode返回是否可以上传。
l 客户端请求第一个 Block上传到哪几个DataNode服务器上。
l NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
l 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
l dn1、dn2、dn3逐级应答客户端。
l 客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
l 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
1.5.2. HDFS读数据流程
l 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
l 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
l DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
l 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
1.6. NN和2NN工作机制(面试重点)
1.6.1. 第一阶段:NameNode启动
l 第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
l 客户端对元数据进行增删改的请求。
l NameNode记录操作日志,更新滚动日志。
l NameNode在内存中对元数据进行增删改。
1.6.2. 第二阶段:Secondary NameNode工作
l Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
l Secondary NameNode请求执行CheckPoint。
l NameNode滚动正在写的Edits日志。
l 将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
l Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
l 生成新的镜像文件fsimage.chkpoint。
l 拷贝fsimage.chkpoint到NameNode。
l NameNode将fsimage.chkpoint重新命名成fsimage。
NN和2NN工作机制详解: Fsimage:NameNode内存中元数据序列化后形成的文件。 Edits:记录客户端更新元数据信息的每一步操作(可通过Edits运算出元数据)。 NameNode启动时,先滚动Edits并生成一个空的edits.inprogress,然后加载Edits和Fsimage到内存中,此时NameNode内存就持有最新的元数据信息。Client开始对NameNode发送元数据的增删改的请求,这些请求的操作首先会被记录到edits.inprogress中(查询元数据的操作不会被记录在Edits中,因为查询操作不会更改元数据信息),如果此时NameNode挂掉,重启后会从Edits中读取元数据的信息。然后,NameNode会在内存中执行元数据的增删改的操作。 由于Edits中记录的操作会越来越多,Edits文件会越来越大,导致NameNode在启动加载Edits时会很慢,所以需要对Edits和Fsimage进行合并(所谓合并,就是将Edits和Fsimage加载到内存中,照着Edits中的操作一步步执行,最终形成新的Fsimage)。SecondaryNameNode的作用就是帮助NameNode进行Edits和Fsimage的合并工作。 SecondaryNameNode首先会询问NameNode是否需要CheckPoint(触发CheckPoint需要满足两个条件中的任意一个,定时时间到和Edits中数据写满了)。直接带回NameNode是否检查结果。SecondaryNameNode执行CheckPoint操作,首先会让NameNode滚动Edits并生成一个空的edits.inprogress,滚动Edits的目的是给Edits打个标记,以后所有新的操作都写入edits.inprogress,其他未合并的Edits和Fsimage会拷贝到SecondaryNameNode的本地,然后将拷贝的Edits和Fsimage加载到内存中进行合并,生成fsimage.chkpoint,然后将fsimage.chkpoint拷贝给NameNode,重命名为Fsimage后替换掉原来的Fsimage。NameNode在启动时就只需要加载之前未合并的Edits和Fsimage即可,因为合并过的Edits中的元数据信息已经被记录在Fsimage中。 |
1.7. DataNode工作机制(面试重点)
l 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
l DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
l 心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
l 集群运行中可以安全加入和退出一些机器。
1.8. DataNode节点保证数据完整性的方法
l 当DataNode读取Block的时候,它会计算CheckSum。
l 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
l Client读取其他DataNode上的Block。
l DataNode在其文件创建后周期验证CheckSum。
1.9. 说说hdfs的文件上传的流程
l 首先客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
l NameNode返回是否可以上传。
l 如果文件大于128M则分块存储,客户端请求第一个 Block上传到哪几个DataNode服务器上。
l NameNode根据副本储存策略返回3个DataNode节点,假如为dn1、dn2、dn3。
l 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求调用dn2,dn2调用dn3,建立通信管道完成,dn1、dn2、dn3逐级应答客户端。
l 客户端以Packet为单位往dn1上传第一个Block数据,dn1收到Packet就会传给dn2,dn2传给dn3;dn1,dn2,dn3每接收packet会放入一个待写队列等待写入数据,落盘。
l 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器,重复执行3-6步。
1.10. 说说文件的下载流程
l 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
l 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
l DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
l 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
1.11. 启动集群时,我们要对namenode进行格式化操作?为什么只能格式化一次
l 因为格式化NameNode,就会产生新的集群id,导致NameNode和DataNode的集群id不一致,集群找不到已往数据(现象datanode无法正常启动);所以,重新格式化NameNode时,一定要先删除data数据和log日志,然后再格式化NameNode,后再启动集群
1.12. 什么情况下集群会进入安全模式?安全模式下集群有有什么限制?如何查看、进入、退出、等待安全模式
l NameNode启动时,会将镜像文件(Fsimage)和编辑日志(Edits)加载到内存。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的Fsimage文件和一个空的编辑日志。此时,NameNode开始监听DataNode请求。这个过程期间,NameNode处于安全模式。当数据块的副本数不满足(dfs.replication.min=1)最小副本数时,不会主动退出安全模式;
l 安全模式下:NameNode的文件系统对于客户端来说是只读的。
l 代码描述
- bin/hdfs dfsadmin -safemode get (功能描述:查看安全模式状态)
- bin/hdfs dfsadmin -safemode enter (功能描述:进入安全模式状态)
- bin/hdfs dfsadmin -safemode leave (功能描述:离开安全模式状态)
- bin/hdfs dfsadmin -safemode wait (功能描述:等待安全模式状态)
l 注意:集群正常启动完成后,自动退出安全模式,如果无法正常退出可使用hdfs dfsadmin -safemode leave退出安全模式;对于全新创建的HDFS集群,NameNode启动后不会进入安全模式,因为没有Block信息。
1.13. 请列出正常工作的Hadoop 集群中Hadoop 都分别需要启动哪些进程,它们的作用分别是什么? 请尽量列的详细一些?
l namenode:
- 负责接受客户端读写数据请求
- 负责数据块副本的存储策略
- 负责管理快数据的映射关系
- 储存元数据信息
l datanode:
- 存储实际的数据块
- 真实处理数据块的读/写操作
l Secondary NameNode:
- 辅助后台程序,与NameNode进行通信,定期合并FSimage和Edits编辑日志,合并为最新的镜像文件。
- 保存HDFS元数据的快照。
l resourcemanager:统一资源调度和管理器
- 处理客户端请求
- 监控NodeManager
- 启动或监控ApplicationMaster
- 资源的分配与调度
l nodemanager:提供计算资源
- 管理单个节点上的资源
- 处理来自ResourceManager的命令
- 处理来自ApplicationMaster的命令
1.14. namenode是怎么确定datanode能够正常工作的?他们之间是怎么保持联系的?
l datanode会先向namdnode注册,namdnode返回注册成功;后每一小时datanode向namenode上传块信息,每3秒datanode向namenode发送一次心跳包,并携带namenode,datanode的命令,默认情况下如果超过10分30秒namenode,没有收到datanode的心跳,则任务datanode掉线。
l dn和nn是通过心跳包来保持联系的
1.15. 说说secondarynamenode的作用和工作机制?
l 2nn并非nn的热备。当nn挂掉的时候,它并不能马上替换nn并提供服务,在紧急情况下,可辅助恢复NameNode
l 2nn辅助nn,分担其工作量,定期合并Fsimage和Edits,并推送给nn,具体如下
l Secondary NameNode工作内容
- 2NN询问NN是否需要CheckPoint(合并镜像和编辑日志),并带回NameNode是否执行结果。
- 2NN请求执行CheckPoint
- NN滚动正在写的Edits编辑日志。
- 将滚动前的编辑日志和镜像文件拷贝到2NN。
- 2NN加载编辑日志和镜像文件到内存,并执行合并,生成新的镜像文件fsimage.chkpoint。
- 2NN拷贝fsimage.chkpoint到NN。
- NN将fsimage.chkpoint重新命名成fsimage,替换之间旧的fsimage
2. Mapreduce
2.1. MapReduce定义
l apReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。
l MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。
2.2. MapReduce优缺点
2.2.1. 优点
l MapReduce 易于编程
- 它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。
l 良好的扩展性
- 当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。
l 高容错性
- MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
l 适合PB级以上海量数据的离线处理
- 可以实现上千台服务器集群并发工作,提供数据处理能力。
2.2.2. 缺点
l 不擅长实时计算
- MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。
l 不擅长流式计算
- 流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
l 不擅长DAG(有向图)计算
- 多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
2.3. MapReduce核心思想
l 分布式的运算程序往往需要分成至少2个阶段。
l 第一个阶段的MapTask并发实例,完全并行运行,互不相干。
l 第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。
l MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。
l 总结:分析WordCount数据流走向深入理解MapReduce核心思想。
2.4. MapReduce进程
l MrAppMaster:负责整个程序的过程调度及状态协调。
l MapTask:负责Map阶段的整个数据处理流程。
l ReduceTask:负责Reduce阶段的整个数据处理流程。
2.5. 常用数据序列化类型
Java类型 |
Hadoop Writable类型 |
Boolean |
BooleanWritable |
Byte |
ByteWritable |
Int |
IntWritable |
Float |
FloatWritable |
Long |
LongWritable |
Double |
DoubleWritable |
String |
Text |
Map |
MapWritable |
Array |
ArrayWritable |
2.6. 序列化概述
2.6.1. 什么是序列化
l 序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输。
l 反序列化就是将收到字节序列(或其他数据传输协议)或者是磁盘的持久化数据,转换成内存中的对象。
2.6.2. 为什么要序列化
l 一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。
2.6.3. 为什么不用Java的序列化
l Java的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header,继承体系等),不便于在网络中高效传输。所以,Hadoop自己开发了一套序列化机制(Writable)。
2.6.4. Hadoop序列化特点:
l 紧凑 :高效使用存储空间。
l 快速:读写数据的额外开销小。
l 可扩展:随着通信协议的升级而可升级
l 互操作:支持多语言的交互
2.7. FileInputFormat切片机制
2.7.1. 切片机制
l 简单地按照文件的内容长度进行切片
l 切片大小,默认等于Block大小
l 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
2.8. CombineTextInputFormat切片机制
l 框架默认的TextInputFormat切片机制是对任务按文件规划切片,不管文件多小,都会是一个单独的切片,都会交给一个MapTask,这样如果有大量小文件,就会产生大量的MapTask,处理效率极其低下。
2.8.1. 应用场景:
l CombineTextInputFormat用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。
2.8.2. 虚拟存储切片最大值设置
l CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);// 4m
l 注意:虚拟存储切片最大值设置最好根据实际的小文件大小情况来设置具体的值。
2.8.3. 切片机制
l 生成切片过程包括:虚拟存储过程和切片过程二部分。
2.9. MapTask工作机制
l Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value。
l Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
l Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
l Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。
l Combine阶段:当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
l 当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。
l 在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并io.sort.factor(默认10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。
l 让每个MapTask最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。
2.10. 溢写阶段详情
l 步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
l 步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。
l 步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。
2.11. ReduceTask工作机制
l Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
l Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
l Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
l Reduce阶段:reduce()函数将计算结果写到HDFS上。
2.12. Join多种应用
2.12.1. Reduce Join工作原理
l Map端的主要工作:为来自不同表或文件的key/value对,打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。
l Reduce端的主要工作:在Reduce端以连接字段作为key的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录(在Map阶段已经打标志)分开,最后进行合并就ok了。
2.13. MR支持的压缩编码
压缩格式 |
hadoop自带? |
算法 |
文件扩展名 |
是否可切分 |
换成压缩格式后,原来的程序是否需要修改 |
DEFLATE |
是,直接使用 |
DEFLATE |
.deflate |
否 |
和文本处理一样,不需要修改 |
Gzip |
是,直接使用 |
DEFLATE |
.gz |
否 |
和文本处理一样,不需要修改 |
bzip2 |
是,直接使用 |
bzip2 |
.bz2 |
是 |
和文本处理一样,不需要修改 |
LZO |
否,需要安装 |
LZO |
.lzo |
是 |
需要建索引,还需要指定输入格式 |
Snappy |
否,需要安装 |
Snappy |
.snappy |
否 |
和文本处理一样,不需要修改 |
2.14. MapReduce 跑的慢的原因
2.14.1. MapReduce 程序效率的瓶颈在于两点
l 计算机性能
- CPU、内存、磁盘健康、网络
l I/O 操作优化
- 数据倾斜
- Map和Reduce数设置不合理
- Map运行时间太长,导致Reduce等待过久
- 小文件过多
- 大量的不可分块的超大文件
- Spill次数过多
- Merge次数过多等。
2.15. MapReduce优化方法
l MapReduce优化方法主要从六个方面考虑:
l 数据输入、
l Map阶段、
l Reduce阶段、
l IO传输、
l 数据倾斜问题和常用的调优参数
2.15.1. 数据输入
l 合并小文件:在执行MR任务前将小文件进行合并,大量的小文件会产生大量的Map任务,增大Map任务装载次数,而任务的装载比较耗时,从而导致MR运行较慢。
l 采用CombineTextInputFormat来作为输入,解决输入端大量小文件场景。
2.15.2. Map阶段
l 减少溢写(Spill)次数:通过调整io.sort.mb及sort.spill.percent参数值,增大触发Spill的内存上限,减少Spill次数,从而减少磁盘IO。
l 减少合并(Merge)次数:通过调整io.sort.factor参数,增大Merge的文件数目,减少Merge的次数,从而缩短MR处理时间。
l 在Map之后,不影响业务逻辑前提下,先进行Combine处理,减少 I/O。
2.15.3. Reduce阶段
l 合理设置Map和Reduce数:两个都不能设置太少,也不能设置太多。太少,会导致Task等待,延长处理时间;太多,会导致Map、Reduce任务间竞争资源,造成处理超时等错误。
l 设置Map、Reduce共存:调整slowstart.completedmaps参数,使Map运行到一定程度后,Reduce也开始运行,减少Reduce的等待时间。
l 规避使用Reduce:因为Reduce在用于连接数据集的时候将会产生大量的网络消耗。
l 合理设置Reduce端的Buffer:默认情况下,数据达到一个阈值的时候,Buffer中的数据就会写入磁盘,然后Reduce会从磁盘中获得所有的数据。也就是说,Buffer和Reduce是没有直接关联的,中间多次写磁盘->读磁盘的过程,既然有这个弊端,那么就可以通过参数来配置,使得Buffer中的一部分数据可以直接输送到Reduce,从而减少IO开销:mapreduce.reduce.input.buffer.percent,默认为0.0。当值大于0的时候,会保留指定比例的内存读Buffer中的数据直接拿给Reduce使用。这样一来,设置Buffer需要内存,读取数据需要内存,Reduce计算也要内存,所以要根据作业的运行情况进行调整。
2.15.4. I/O传输
l 采用数据压缩的方式,减少网络IO的的时间。安装Snappy和LZO压缩编码器。
l 使用SequenceFile二进制文件。
2.15.5. 数据倾斜问题
2.15.5.1. 数据倾斜现象
l 数据频率倾斜——某一个区域的数据量要远远大于其他区域。
l 数据大小倾斜——部分记录的大小远远大于平均值。
2.15.5.2. 减少数据倾斜的方法
2.15.5.2.1. 方法一:抽样和范围分区
l 可以通过对原始数据进行抽样得到的结果集来预设分区边界值。
2.15.5.2.2. 方法二:自定义分区
l 基于输出键的背景知识进行自定义分区。例如,如果Map输出键的单词来源于一本书。且其中某几个专业词汇较多。那么就可以自定义分区将这这些专业词汇发送给固定的一部分Reduce实例。而将其他的都发送给剩余的Reduce实例。
2.15.5.2.3. 方法三:Combine
l 使用Combine可以大量地减小数据倾斜。在可能的情况下,Combine的目的就是聚合并精简数据。
l 方法四:采用Map Join,尽量避免Reduce Join
2.16. 小文存在的弊端(坏处)? 如何解决?
2.16.1. 弊端
l HDFS上每个文件都要在NameNode上建立一个索引,这个索引的大小约为150byte,这样当小文件比较多的时候,就会产生很多的索引文件,一方面会大量占用NameNode的内存空间,另一方面就是索引文件过大使得索引速度变慢。
2.16.2. 解决方案一
l 在数据采集的时候,就将小文件或小批数据合成大文件再上传HDFS。
l 在业务处理之前,在HDFS上使用MapReduce程序对小文件进行合并。
l 在MapReduce处理时,可采用CombineTextInputFormat提高效率。
2.16.3. 解决方案二
2.16.3.1. Hadoop Archive
l 是一个高效地将小文件放入HDFS块中的文件存档工具,它能够将多个小文件打包成一个HAR文件,这样就减少了NameNode的内存使用。
2.16.3.2. Sequence File
l Sequence File由一系列的二进制key/value组成,如果key为文件名,value为文件内容,则可以将大批小文件合并成一个大文件。
2.16.3.3. CombineFileInputFormat
l CombineFileInputFormat是一种新的InputFormat,用于将多个文件合并成一个单独的Split,另外,它会考虑数据的存储位置。
2.16.4. 解决方案三
2.16.4.1. 开启JVM重用
l 对于大量小文件Job,可以开启JVM重用会减少45%运行时间。
l JVM重用原理:一个Map运行在一个JVM上,开启重用的话,该Map在JVM上运行完毕后,JVM继续运行其他Map。
l 具体设置:mapreduce.job.jvm.numtasks值在10-20之间。
3. Yarn
3.1. 工作机制详解
l MR程序提交到客户端所在的节点。
l YarnRunner向ResourceManager申请一个Application。
l RM将该应用程序的资源路径返回给YarnRunner。
l 该程序将运行所需资源提交到HDFS上。
l 程序资源提交完毕后,申请运行mrAppMaster。
l RM将用户的请求初始化成一个Task。
l 其中一个NodeManager领取到Task任务。
l 该NodeManager创建容器Container,并产生MRAppmaster。
l Container从HDFS上拷贝资源到本地。
l MRAppmaster向RM 申请运行MapTask资源。
l RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器。
l MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序。
l MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。
l ReduceTask向MapTask获取相应分区的数据。
l 程序运行完毕后,MR会向RM申请注销自己。
3.2. 作业提交全过程详解
3.2.1. 作业提交
l 作业提第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。
l 第2步:Client向RM申请一个作业id。
l 第3步:RM给Client返回该job资源的提交路径和作业id。
l 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。
l 第5步:Client提交完资源后,向RM申请运行MrAppMaster。
3.2.2. 作业初始化
l 第6步:当RM收到Client的请求后,将该job添加到容量调度器中。
l 第7步:某一个空闲的NM领取到该Job。
l 第8步:该NM创建Container,并产生MRAppmaster。
l 第9步:下载Client提交的资源到本地。
3.2.3. 任务分配
l 第10步:MrAppMaster向RM申请运行多个MapTask任务资源。
l 第11步:RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器。
3.2.4. 任务运行
l 第12步:MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序。
l 第13步:MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。
l 第14步:ReduceTask向MapTask获取相应分区的数据。
l 第15步:程序运行完毕后,MR会向RM申请注销自己。
3.2.5. 进度和状态更新
l YARN中的任务将其进度和状态(包括counter)返回给应用管理器, 客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应用管理器请求进度更新, 展示给用户。
3.2.6. 作业完成
l 除了向应用管理器请求作业进度外, 客户端每5秒都会通过调用waitForCompletion()来检查作业是否完成。时间间隔可以通过mapreduce.client.completion.pollinterval来设置。作业完成之后, 应用管理器和Container会清理工作状态。作业的信息会被作业历史服务器存储以备之后用户核查。
3.3. 资源调度器
3.3.1. 目前,Hadoop作业调度器主要有三种:
l FIFO、Capacity Scheduler和Fair Scheduler。
l Hadoop2.7.2默认的资源调度器是Capacity Scheduler。
3.3.2. 先进先出调度器(FIFO)
3.3.3. 容量调度器(Capacity Scheduler)
3.3.4. 公平调度器(Fair Scheduler)
3.4. 在搭建集群的过程中我们需要开启哪些进程服务才能确保集群能够正常的读取数据、以及在Yarn上完成计算任务?
l namenode datanode secondarynamenode
l resourcemanager nodemanager