本文包含HDFS、Yarn和MapReduce三部分内容,总结了Hadoop的NameNode、2NN、DataNode原理,HDFS存储机制,MapReduce流程,数据倾斜,Yarn工作机制等相关知识要点。
一、HDFS部分
1、NameNode和SecondaryNameNode工作机制
第一阶段:NameNode 启动
(1)第一次启动 NameNode 格式化后,创建 Fsimage 和 Edits 文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode 记录操作日志,更新滚动日志。
(4)NameNode 在内存中对元数据进行增删改。
第二阶段:Secondary NameNode 工作
(1)Secondary NameNode 询问 NameNode 是否需要 CheckPoint。直接带回 NameNode 是否检查结果。
(2)Secondary NameNode 请求执行 CheckPoint。
(3)NameNode 滚动正在写的 Edits 日志。
(4)将滚动前的编辑日志和镜像文件拷贝到 Secondary NameNode 。
(5)Secondary NameNode 加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件 fsimage.chkpoint。
(7)拷贝 fsimage.chkpoint 到 NameNode。
(8)NameNode 将 fsimage.chkpoint 重新命名成 fsimage 。
2、NameNode高可用原理
- 原理
在一个典型的高可用集群中,每个 NameNode 是一台独立的服务器。在任一时刻,只有一个 NameNode 处于 active 状态,其他的 NameNode 处于 standby 状态。其中,active 状态的 NameNode 负责所有的客户端操作, standby 状态的 NameNode 处于从属地位,维护着数据状态,随时准备切换。两个 NameNode 为了数据同步,会通过一组称作 JournalNodes 的独立进程进行相互通信。当 active 状态的 NameNode 的命名空间有任何修改时,会告知大部分的 JournalNodes 进程。standby 状态的 NameNode 有能力读取 JournalNodes 中的变更信息,并且一直监控 edit log 的变化,把变化应用于自己的命名空间。standby 可以确保在集群出错时,命名空间状态已经完全同步。 为了确保快速切换,standby 状态的 NameNode 有必要知道集群中所有数据块的位置。为了做到这点,所有的 Datanode 必须配置两个 NameNode 的地址,发送数据块位置信息和心跳给他们两个。对于 HA 集群而言,确保同一时刻只有一个 NameNode 处于 active 状态是至关重要的。否则,两个 NameNode 的数据状态就会产生分歧,可能丢失数据,或者产生错误的结果。为了保证这点,JournalNodes 必须确保同一时刻只有一个 NameNode 可以向自己写数据。
- HDFS-HA 核心问题
(1)怎么保证三台 NameNode 的数据一致
a.Fsimage文件:让一台 NameNode 生成数据,让其他机器 nn 同步
b.Edits文件: 需要引进新的模块 JournalNode 来保证 edtis 的文件的数据一致性
(2)怎么让同时只有一台 NameNode 是 active,其他所有是 standby 的
a.手动分配
b.自动分配
(3)2nn 在 HA 架构中并不存在,定期合并 fsimage 和 edtis 的活谁来干
由 standby 的 NameNode 来干
(4)如果 NameNode 真的发生了问题,怎么让其他的 NameNode 上位干活
a.手动故障转移
b.自动故障转移
- 自动故障转移机制
- HDFS-HA 自动故障转移的集群规划示例
3、DataNode工作机制
(1)一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode 启动后向 NameNode 注册,通过后,周期性(1小时)地向 NameNode 上报所有的块信息。
(3)心跳是每3秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个 DataNode 的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
4、数据压缩
- gzip压缩
应用场景:当每个文件压缩之后在 130M 以内的(1个块大小内),都可以考虑用 gzip 压缩格式。譬如说 一天或者一个小时的日志压缩成一个 gzip 文件,运行 mapreduce 程序的时候通过多个 gzip 文件达到并发。hive 程序,streaming 程序,和 java 写的 mapreduce 程序完全和文本处理一样,压缩之后原来的程序不需要做任何修改。
优点:压缩率比较高,而且压缩/解压速度也比较快;hadoop 本身支持,在应用中处理 gzip 格式的文件 就和直接处理文本一样;有 hadoop native 库;大部分 linux 系统都自带 gzip 命令,使用方便。
缺点:不支持 split。
- snappy压缩
应用场景:当 mapreduce 作业的 map 输出的数据比较大的时候,作为 map 到 reduce 的中间数据的压缩格式;或者作为一个 mapreduce 作业的输出和另外一个 mapreduce 作业的输入。
优点:高速压缩速度和合理的压缩率;支持 hadoop native 库。
缺点:不支持 split;压缩率比 gzip 要低;hadoop 本身不支持,需要安装;linux 系统下没有对应的命令。
- lzo压缩
应用场景:一个很大的文本文件,压缩之后还大于 200M 以上的可以考虑,而且单个文件越大,lzo 优点越明显。
优点:压缩/解压速度也比较快,合理的压缩率;支持 split,是 hadoop 中最流行的压缩格式;支持 hadoop native库;可以在linux系统下安装 lzop 命令,使用方便。
缺点:压缩率比 gzip 要低一些;hadoop 本身不支持,需要安装;在应用中对 lzo 格式的文件需要做一些特殊处理(为了支持 split 需要建索引,还需要指定 inputformat 为 lzo 格式)。
- bzip2压缩
应用场景:适合对速度要求不高,但需要较高的压缩率的时候,可以作为 mapreduce 作业的输出格式; 或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情 况;或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持 split,而且兼容之前的应用程序 (即应用程序不需要修改)的情况。
优点:支持 split;具有很高的压缩率,比 gzip 压缩率都高;hadoop 本身支持,但不支持 native;在linux 系统下自带 bzip2 命令,使用方便。
缺点:压缩/解压速度慢。
5、HDFS存储机制之写过程
(1)客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件,NameNode 检查目标文件是否已存在,父目录是否存在。
(2)NameNode 返回是否可以上传。
(3)客户端请求第一个 Block 上传到哪几个 DataNode 服务器上。
(4)NameNode 返回3个 DataNode 节点,分别为 dn1、dn2、dn3。
(5)客户端通过 FSDataOutputStream 模块请求 dn1 上传数据,dn1 收到请求会继续调用 dn2,然后 dn2调用 dn3,将这个通信管道建立完成。
(6)dn1、dn2、dn3 逐级应答客户端。
(7)客户端开始往 dn1 上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存),以 Packet 为单 位,dn1收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 传一个 packet 会放入一个应答队列等待应答。
(8)当一个 Block 传输完成之后,客户端再次请求 NameNode 上传第二个 Block 的服务器
(重复执行3-7步)。
6、HDFS存储机制之读过程
(1)客户端通过 Distributed FileSystem 向 NameNode 请求下载文件,NameNode 通过查询元数据, 找到文件块所在的 DataNode 地址。
(2)挑选一台 DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以 Packet 为单位来做校验)。
(4)客户端以 Packet 为单位接收,先在本地缓存,然后写入目标文件。
7、HDFS小文件处理
- HDFS小文件弊端
HDFS上每个文件都要在 NameNode 上建立一个索引,这个索引的大小约为 150byte,这样当小文件比较 多的时候,就会产生很多的索引文件,一方面会大量占用 NameNode 的内存空间,另一方面就是索引文 件过大使得索引速度变慢。
- HDFS小文件解决方案
(1) Hadoop Archive: Hadoop Archive 或者 HAR,是一个高效地将小文件放入 HDFS 块中的文件存档工具,它能够将多个小文件打包成一个 HAR 文件,这样在减少 namenode 内存使用的同时,仍然允许对文件进行透明的访问。
(2) Sequence file: sequence file由一系列的二进制 key/value 组成,如果为key小文件名,value为文件内容,则可以将大批小文件合并成一个大文件。
(3)CombineFileInputFormat 用于将多个文件合并出成单独的 Split,另外,它会考虑数据的存储位置。
(4)开启 JVM 重用原理:一个 Map 运行在一个 JVM 上,开启重用:该 Map 在 JVM 上运行完毕后,JVM 继续运行其他 Map(mapreduce.job.jvm.numtasks)。 对于大量小文件 Job ,可以减少45%运行时间。
二、MapReduce部分
1、MapReduce流程
1、MapTask 通过用户编写的 RecordReader,从输入 InputSplit 中解析出一个个 key/value 。
2、将解析出的 key/value 交给用户编写 map() 方法处理,并产生一系列新的 key/value 。
3、调用 OutputCollector.collect() 输出结果。在该函数内部,它会将生成的 key/value 分区(调用 Partitioner),然后写入一个默认100MB大小的环形内存缓冲区中,该环形缓冲区一半存数据一半存索引(元数据)。
4、当环形缓冲区写到 80% 时开始溢写到磁盘上,溢写过程中新到的数据反向写入。
5、数据分区内对 key 的索引按字典顺序进行快速排序。如果设置了 Combiner ,则对每个分区中的数据进行一次聚集操作。
6、对分区内部有序的临时数据文件进行归并排序,确保一个 MapTask 最后只产生一个数据文件。此时可以根据设置进行第二次 Combiner 合并以及压缩操作,可以提高网络传输效率。
7、ReduceTask 根据自己的分区号,去各个 MapTask 机器上取相应的结果分区数据。如果数据片大小超过一定阈值,就写到磁盘上,否则直接放到内存中。
8、在远程拷贝数据的同时,ReduceTask 启动两个后台线程对内存和磁盘上的文件进行归并排序合并,以防止内存使用过多或磁盘上文件过多。
9、对数据按 key 进行分组,保证 reduce 方法一次处理相同 key 的数据。
10、聚合运算结果由 OutPutFormat 组件中的 RecordWriter 写到 HDFS 上。
2、优化
- Map 阶段优化
(1)增大环形缓冲区大小。由100m扩大到200m
(2)增大环形缓冲区溢写的比例。由80%扩大到90%
(3)减少对溢写文件的 merge 次数。(10个文件,一次20个merge)
(4)不影响实际业务的前提下,采用 Combiner 提前合并,减少 I/O。
- Reduce 阶段优化
(1)合理设置 Map 和 Reduce 数:两个都不能设置太少,也不能设置太多。太少,会导致 Task 等待,延长处理时间;太多,会导致 Map、Reduce 任务间竞争资源,造成处理超时等错误。
(2)设置 Map、Reduce 共存:调整 slowstart.completedmaps 参数,使 Map 运行到一定程度后, Reduce也开始运行,减少 Reduce 的等待时间。
(3)规避使用 Reduce ,因为 Reduce 在用于连接数据集的时候将会产生大量的网络消耗。
(4)增加每个 Reduce 去 Map 中拿数据的并行数。
(5)集群性能可以的前提下,增大 Reduce 端存储数据内存的大小。
- IO 传输
采用数据压缩的方式,减少网络IO的时间。安装 Snappy 和 LZOP 压缩编码器。
压缩:
(1)map 输入端主要考虑数据量大小和切片,支持切片的有 Bzip2 、LZO。注意:LZO要想支持切片必须创建索引。
(2)map 输出端主要考虑速度,速度快的 snappy 、LZO。
(3)reduce 输出端主要看具体需求,例如作为下一个 mr 输入需要考虑切片,永久保存考虑压缩率比较大的gzip。
3、数据倾斜
1、定义
mapreduce 程序执行时,reduce 节点大部分执行完毕,但是有一个或者几个 reduce 节点运行很慢,导致整个程序的处理时间很长,这是因为某一个 key 的条数比其他 key 多很多(有时是百倍或者千倍之多),这条 key 所在的 reduce 节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完。
2、现象
1、绝大多数 task 执行得都非常快,但个别 task 执行的极慢。
2、原本能正常执行的 Spark 作业,某天突然爆出 OOM(内存溢出)异常。观察异常栈,是我们写的业务代码造成的。
3、解决方案
1、能在 map 阶段提前处理,最好先在 Map 阶段处理
提前在 map 进行 combine 和 MapJoin,减少传输的数据量。在 Mapper 加上 combiner 相当于提前进行 reduce ,即把一个 Mapper 中的相同 key 进行了聚合,减少 shuffle 过程中传输的数据量,以及 Reducer 端的计算量。 如果导致数据倾斜的 key 大量分布在不同的 mapper 的时候,这种方法就不是很有效了。
2、导致数据倾斜的 key 大量分布在不同的 mapper
(1)局部聚合加全局聚合。 第一次在 map 阶段对那些导致了数据倾斜的 key 加上1到n的随机前缀,这样本来相同的 key 也会被分到多个 Reducer 中进行局部聚合,数量就会大大降低。 第二次 mapreduce,去掉 key 的随机前缀,进行全局聚合。思想:二次 mr ,第一次将 key 随机散列到不同reducer进行处理达到负载均衡目的。第二次再根据去掉 key 的随机前缀,按原 key 进行 reduce 处理。 这个方法进行两次 mapreduce,性能稍差。
(2) 增加 Reducer,提升并行度 : JobConf.setNumReduceTasks(int)
(3)实现自定义分区:根据数据分布情况,自定义散列函数,将 key 均匀分配到不同 Reducer。
3、增加 reduce 内存
这适用于唯一值非常少,极少数值有非常多的记录值(唯一值少于几千)。这种情况下,往往只能通过硬件的手段来进行调优,增加 jvm 内存可以显著的提高运行效率。
4、增加 reduce 个数
这适用于唯一值比较多,这个字段的某些值有远远多于其他值的记录数,但是它的占比也小于百分之一或千分之一。我们知道,这种情况下,最容易造成的结果就是大量相同 key 被 partition 到一个分区,从而一个 reduce 执行了大量的工作,而如果我们增加了 reduce 的个数,这种情况相对来说会减轻很多,毕竟计算的节点多了,就算工作量还是不均匀的,那也要小很多。
三、Yarn部分
1、Yarn工作机制
2、Yarn调度器
1、先进先出调度器(FIFO)
单队列,根据提交作业的先后顺序,先来先服务。
优点:简单易懂;
缺点:不支持多队列,生产环境很少使用。
2、容量调度器
以队列为单位划分资源,每个队列可设定一定比例的资源最低保证和使用上限,同时,每个用户也可设定一定的资源使用上限以防止资源滥用。而当一个队列的资源有剩余时,可暂时将剩余资源共享给其他队列。
特点
(1)可为每个队列设置资源最低保证和资源使用上限,而所有提交到该队列的应用程序共享这些资源。
(2)如果一个队列中的资源有剩余,可以暂时共享给那些需要资源的队列,而一旦该队列有新的应用 程序提交,则其他队列释放的资源会归还给该队列。
(3)支持多用户共享集群和多应用程序同时运行。为防止单个应用程序、用户或队列独占集群中的资 源,可为之增加多重约束(比如单个应用程序同时运行的任务数等)。
(4)每个队列有严格的ACL列表规定它的访问用户,每个用户可指定哪些用户允许查看自己应用程序的 运行状态或者控制应用程序(比如杀死应用程序)。
3、公平调度器
公平调度器可以为所有的应用“平均公平”分配资源,当然,这种“公平”是可以配置的,称为权重,可以在分配文件中为每一个队列设置分配资源的权重,如果没有设置,默认是1(由于默认权重相同,因此在不做配置的情况下,作业(队列)之间的资源占比相同)。 公平调度器设计目标是:在时间尺度上,所有作业获得公平的资源。某一时刻一个作业应获资源和实际获取资源的差距叫“缺额”,调度器会优先为缺额大的作业分配资源。
特点:
(1)允许资源共享,即当一个 APP 运行时,如果其它队列没有任务执行,则可以使用其它队列(不属于该 APP 的队列),当其它队列有APP需要资源时再将占用的队列释放出来.所有的 APP 都从资源队列中分配资源。
(2)当队列中有任务,该队列将至少获得最小资源;当队列资源使用不完时可,以给其它队列使用。
(3)当队列不能满足最小资源时,可以从其它队列抢占。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。