本文主要是介绍3.1.4 Hadoop, Yarn, 资源调度, AH源码分析, 3.x 新特性概述, Job三原则, 调优(Shuffle, Job, YARN, NN Full GC), 二次开发环境搭建,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
第七部分 YARN资源调度
第 1 节 Yarn架构
第 2 节 Yarn任务提交(⼯作机制)
第 3 节 Yarn调度策略
第 4 节 Yarn多租户资源隔离配置
第⼋部分 Apache Hadoop HDFS核⼼源码剖析
第 1 节 源码阅读准备
第 2 节 NameNode 启动流程
第 3 节 DataNode 启动流程
第 4 节 写数据流程
第 5 节 NameNode如何⽀撑⾼并发访问(双缓冲机制)
扩展 Hadoop 3.x 新特性概述
Hadoop3.x新特性之Common改进
Hadoop3.x新特性之HDFS改进
Hadoop3.x新特性之YARN改进
Hadoop3.x新特性之MapReduce改进
Hadoop3.x新特性之其他
第九部分 调优及二次开发示例
第一节 Job执行三原则
1.1 原则一 充分利用集群资源
1.2 原则二 ReduceTask并发调整
1.3 原则三 Task执行时间要合理
第二节 Shuffle调优
2.1 Map阶段
2.2 Copy阶段
2.3 Reduce阶段
第三节 Job调优
1、推测执行
2、Slow Start
3 、小文件优化
4 、数据倾斜
第四节 YARN调优
1、NM配置
2、Container启动模式
3、AM调优
第五节 Namenode Full GC
1、对象分代
2、Jstat
3、GC 日志解析
第六节 Hadoop二次开发环境搭建
第七部分 YARN资源调度
第 1 节 Yarn架构
ResourceManager(rm):处理客户端请求、启动/监控ApplicationMaster、监控NodeManager、资源分配与调度;
NodeManager(nm):单个节点上的资源管理、处理来⾃ResourceManager的命令、处理来⾃ApplicationMaster的命令;
ApplicationMaster(am):数据切分、为应⽤程序申请资源,并分配给内部任务、任务监控与容错。
Container:对任务运⾏环境的抽象,封装了CPU、内存等多维资源以及环境变量、启动命令等任务运⾏相关的信息。
第 2 节 Yarn任务提交(⼯作机制)
作业提交过程之YARN (要能复述出来)
作业提交
第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中的任务将其进度和状态返回给应⽤管理器, 客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应⽤管理器请求进度更新, 展示给⽤户。
作业完成
除了向应⽤管理器请求作业进度外, 客户端每5秒都会通过调⽤waitForCompletion()来检查作业是否完成。
时间间隔可以通过mapreduce.client.completion.pollinterval来设置。
作业完成之后, 应⽤管理器和Container会清理⼯作状态。作业的信息会被作业历史服务器存储以备之后⽤户核查。
第 3 节 Yarn调度策略
Hadoop作业调度器主要有三种:FIFO、Capacity Scheduler和Fair Scheduler。
Hadoop2.9.2默认的资源调度器是Capacity Scheduler。
可以查看yarn-default.xml
1. FIFO(先进先出调度器)
2. 容量调度器(Capacity Scheduler 默认的调度器)
Apache Hadoop默认使⽤的调度策略。Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的⼀部分计算能⼒。通过为每个组织分配专⻔的队列,然后再为每个队列分配⼀定的集群资源,这样整个集群就可以通过设置多个队列的⽅式给多个组织提供服务了。除此之外,队列内部⼜可以垂直划分,这样⼀个组织内部的多个成员就可以共享这个队列资源了,在⼀个队列内部,资源的调度是采⽤的是先进先出(FIFO)策略。
3. Fair Scheduler(公平调度器,CDH版本的hadoop默认使⽤的调度器)
Fair调度器的设计⽬标是为所有的应⽤分配公平的资源(对公平的定义可以通过参数来设置)。公平调度在也可以在多个队列间⼯作。
举个例⼦,假设有两个⽤户A和B,他们分别拥有⼀个队列。
当A启动⼀个job⽽B没有任务时,A会获得全部集群资源;当B启动⼀个job后,A的job会继续运⾏,不过⼀会⼉之后两个任务会各⾃获得⼀半的集群资源。如果此时B再启动第⼆个job并且其它job还在运⾏,则它将会和B的第⼀个job共享B这个队列的资源,也就是B的两个job会⽤于四分之⼀的集群资源,⽽A的job仍然⽤于集群⼀半的资源,结果就是资源最终在两个⽤户之间平等的共享
第 4 节 Yarn多租户资源隔离配置
Yarn集群资源设置为A,B两个队列,
A队列设置占⽤资源70%主要⽤来运⾏常规的定时任务,
B队列设置占⽤资源30%主要运⾏临时任务,
两个队列间可相互资源共享,假如A队列资源占满,B队列资源⽐较充裕,A队列可以使⽤B队列的资源,使总体做到资源利⽤最⼤化
选择使⽤Fair Scheduler调度策略!!
具体配置
1. yarn-site.xml
<!-- 指定任务调度使⽤fairScheduler的调度⽅式 -->
<property><name>yarn.resourcemanager.scheduler.class</name><value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value><description>In case you do not want to use the default scheduler</description>
</property>
2. 创建fair-scheduler.xml⽂件
在Hadoop安装⽬录/etc/hadoop创建该⽂件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<allocations><defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy><queue name="root" ><queue name="default"><aclAdministerApps>*</aclAdministerApps><aclSubmitApps>*</aclSubmitApps><maxResources>9216 mb,4 vcores</maxResources><maxRunningApps>100</maxRunningApps><minResources>1024 mb,1vcores</minResources><minSharePreemptionTimeout>1000</minSharePreemptionTimeout><schedulingPolicy>fair</schedulingPolicy><weight>7</weight></queue><queue name="queue1"><aclAdministerApps>*</aclAdministerApps><aclSubmitApps>*</aclSubmitApps><maxResources>4096 mb,4vcores</maxResources><maxRunningApps>5</maxRunningApps><minResources>1024 mb, 1vcores</minResources><minSharePreemptionTimeout>1000</minSharePreemptionTimeout><schedulingPolicy>fair</schedulingPolicy><weight>3</weight></queue></queue> <queuePlacementPolicy><rule create="false" name="specified"/><rule create="true" name="default"/></queuePlacementPolicy>
</allocations>
界⾯验证
第⼋部分 Apache Hadoop HDFS核⼼源码剖析
第 1 节 源码阅读准备
1. 下载Apache Hadoop-2.9.2官⽅源码
2. 将源码导⼊idea中
启动idea在提示界⾯选择导⼊
等待下载和解决依赖完成,源码导⼊成功!!
第 2 节 NameNode 启动流程
命令启动Hdfs集群
start-dfs.sh
该命令会启动Hdfs的NameNode以及DataNode,启动NameNode主要是通过
org.apache.hadoop.hdfs.server.namenode.NameNode类。
我们重点关注NameNode在启动过程中做了哪些⼯作(偏离主线的技术细节不深究)
对于分析启动流程主要关注两部分代码:
namenode的主要责任是⽂件元信息与数据块映射的管理。相应的,namenode的启动流程需要关注与客户端、datanode通信的⼯作线程,⽂件元信息的管理机制,数据块的管理机制等。其中,RpcServer主要负责与客户端、datanode通信,FSDirectory主要负责管理⽂件元信息。
第 3 节 DataNode 启动流程
datanode的Main Class是DataNode,先找到DataNode.main()
第 4 节 写数据流程
datanode上有很多重要的⼯作线程。其中,与写数据块过程联系最紧密的是DataXceiverServer与BPServiceActor。客户端与数据节点间主要通过流接⼝DataTransferProtocol完成数据块的读/写。
DataTransferProtocol⽤于整个管道中的客户端、数据节点间的流式通信,其中,DataTransferProtocol--writeBlock()负责完成写数据块的⼯作:
第 5 节 NameNode如何⽀撑⾼并发访问(双缓冲机制)
⾼并发访问NameNode会遇到什么样的问题:
经过学习HDFS的元数据管理机制,Client每次请求NameNode修改⼀条元数据
(⽐如说申请上传⼀个⽂件,都要写⼀条edits log,包括两个步骤:
写⼊本地磁盘--edits⽂件
通过⽹络传输给JournalNodes集群(Hadoop HA集群--结合zookeeper来学习)。
⾼并发的难点主要在于数据的多线程安全以及每个操作效率!!
对于多线程安全:
NameNode在写edits log时⼏个原则:
写⼊数据到edits_log必须保证每条edits都有⼀个全局顺序递增的transactionId(简称为txid),这样才可以标识出来⼀条⼀条的edits的先后顺序。
如果要保证每条edits的txid都是递增的,就必须得加同步锁。也就是每个线程修改了元数据,要写⼀条edits 的时候,都必须按顺序排队获取锁后,才能⽣成⼀个递增的txid,代表这次要写的edits的序号。
产⽣的问题:
如果每次都是在⼀个加锁的代码块⾥,⽣成txid,然后写磁盘⽂件edits log,这种既有同步锁⼜有写磁盘操作⾮常耗时!!
HDFS优化解决⽅案
问题产⽣的原因主要是在于,写edits时串⾏化排队⽣成⾃增txid + 写磁盘操作费时,
HDFS的解决⽅案
1. 串⾏化:使⽤分段锁
2. 写磁盘:使⽤双缓冲
分段加锁机制 ⾸先各个线程依次第⼀次获取锁,⽣成顺序递增的txid,然后将edits写⼊内存双缓冲的区域1,接着就⽴⻢第⼀次释放锁了。趁着这个空隙,后⾯的线程就可以再次⽴⻢第⼀次获取锁,然后⽴即写⾃⼰的edits到内存缓冲。
双缓冲机制 程序中将会开辟两份⼀模⼀样的内存空间,⼀个为bufCurrent,产⽣的数据会直接写⼊到这个bufCurrent,⽽另⼀个叫bufReady,在bufCurrent数据写⼊(达到⼀定标准)后,两⽚内存就会exchange(交换)。直接交换双缓冲的区域1和区域2。保证接收客户端写⼊数据请求的都是操作内存, ⽽不是同步写磁盘。
双缓冲源码分析
扩展 Hadoop 3.x 新特性概述
Hadoop3.x中增强了很多特性,在Hadoop3.x中,不再允许使⽤jdk1.7,要求jdk1.8以上版本。这是因为Hadoop 2.0是基于JDK 1.7开发的,⽽JDK 1.7在2015年4⽉已停⽌更新,这直接迫使Hadoop社区基于JDK 1.8重新发布⼀个新的Hadoop版本,⽽这正是Hadoop3.x。Hadoop3.x以后将会调整⽅案架构,将Mapreduce 基于内存+io+磁盘,共同处理数据。
Hadoop 3.x中引⼊了⼀些重要的功能和优化,包括HDFS 可擦除编码、多Namenode⽀持、MR NativeTask优化、YARN基于cgroup的内存和磁盘IO隔离、YARN container resizing等。
Hadoop3.x官⽅⽂档地址如下:
http://hadoop.apache.org/docs/r3.0.1/
Hadoop3.x新特性之Common改进
Hadoop Common改进:
1. 精简Hadoop内核,包括剔除过期的API和实现,将默认组件实现替换成最⾼效的实现(⽐如将FileOutputCommitter缺省实现换为v2版本,废除hftp转由webhdfs替代,移除Hadoop⼦实现序列化库org.apache.hadoop.Records
2. lasspath isolation以防⽌不同版本jar包冲突,⽐如google Guava在混合使⽤Hadoop、HBase和Spark时,很容易产⽣冲突.(https://issues.apache.org/jira/browse/HADOOP-11656)
3. Shell脚本重构。 Hadoop 3.0对Hadoop的管理脚本进⾏了重构,修复了⼤量bug,增加了新特性,⽀持动态命令等。使⽤⽅式上则和之前版本的⼀致。(https://issues.apache.org/jira/browse/HADOOP-9902)
Hadoop3.x新特性之HDFS改进
Hadoop3.x中最⼤改变的是HDFS,HDFS通过最近black块计算,根据最近计算原则,本地black块,加⼊到内存,先计算,通过IO,共享内存计算区域,最后快速形成计算结果。
1. HDFS⽀持数据的擦除编码,这使得HDFS在不降低可靠性的前提下,节省⼀半存储空间。(https://issues.apache.org/jira/browse/HDFS-7285)
2. 多NameNode⽀持,即⽀持⼀个集群中,⼀个active、多个standby namenode部署⽅式。注:多ResourceManager特性在hadoop 2.0中已经⽀持。(https://issues.apache.org/jira/browse/HDFS-6440)
关于这两个特性的官⽅⽂档地址:
http://hadoop.apache.org/docs/r3.0.1/hadoop-project-dist/hadoop-hdfs/HDFSErasureCoding.html
http://hadoop.apache.org/docs/r3.0.1/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html
Hadoop3.x新特性之YARN改进
1. 基于cgroup的内存隔离和IO Disk隔离(https://issues.apache.org/jira/browse/YARN-2619)
2. ⽤curator实现RM leader选举(https://issues.apache.org/jira/browse/YARN-4438)
3. containerresizing(https://issues.apache.org/jira/browse/YARN-1197)
4. Timelineserver next generation (https://issues.apache.org/jira/browse/YARN-2928)
官⽅⽂档地址:
http://hadoop.apache.org/docs/r3.0.1/hadoop-yarn/hadoop-yarn-site/TimelineServiceV2.html
Hadoop3.x新特性之MapReduce改进
1. Tasknative优化。为MapReduce增加了C/C++的map output collector实现(包括Spill,Sort和IFile等),通过作业级别参数调整就可切换到该实现上。对于shuffle密集型应⽤,其性能可提⾼约30%。(https://issues.apache.org/jira/browse/MAPREDUCE-2841)
2. MapReduce内存参数⾃动推断。在Hadoop 2.0中,为MapReduce作业设置内存参数⾮常繁琐,涉及到两个参数:mapreduce.{map,reduce}.memory.mb和mapreduce.{map,reduce}.java.opts,⼀旦设置不合理,则会使得内存资源浪费严重,⽐如将前者设置为4096MB,但后者却是“-Xmx2g”,则剩余2g实际上⽆法让java heap使⽤到。(https://issues.apache.org/jira/browse/MAPREDUCE-5785)
Hadoop3.x新特性之其他
1. 添加新的 hadoop-client-api 和 hadoop-client-runtime 组件到⼀个单ᇿ的jar包⾥,以此解决依赖不兼容的问题.(https://issues.apache.org/jira/browse/HADOOP-11804)
2. ⽀持微软的Azure分布式⽂件系统和阿⾥的aliyun分布式⽂件系统
第九部分 调优及二次开发示例
第一节 Job执行三原则
- 充分利用集群资源
- reduce阶段尽量放在一轮
- 每个task的执行时间要合理
1.1 原则一 充分利用集群资源
Job运行时,尽量让所有的节点都有任务处理,这样能尽量保证集群资源被充分利用,任务的并发度达到最大。可以通过调整处理的数据量大小,以及调整map和reduce个数来实现。
- Reduce个数的控制使用“mapreduce.job.reduces”
- Map个数取决于使用了哪种InputFormat,默认的TextFileInputFormat将根据block的个数来分配map数(一个block一个map)。
1.2 原则二 ReduceTask并发调整
努力避免出现以下场景
- 观察Job如果大多数ReduceTask在第一轮运行完后,剩下很少甚至一个ReduceTask刚开始运行。这种情况下,这个ReduceTask的执行时间将决定了该job的运行时间。可以考虑将reduce个数减少。
- 观察Job的执行情况如果是MapTask运行完成后,只有个别节点有ReduceTask在运行。这时候集群资源没有得到充分利用,需要增加Reduce的并行度以便每个节点都有任务处理。
1.3 原则三 Task执行时间要合理
一个job中,每个MapTask或ReduceTask的执行时间只有几秒钟,这就意味着这个job的大部分时间都消耗在task的调度和进程启停上了,因此可以考虑增加每个task处理的数据大小。建议一个task处理时间为1分钟。
第二节 Shuffle调优
Shuffle阶段是MapReduce性能的关键部分,包括了从MapTaskask将中间数据写到磁盘一直到ReduceTask拷贝数据并最终放到
Reduce函数的全部过程。这一块Hadoop提供了大量的调优参数。
2.1 Map阶段
1、判断Map内存使用
判断Map分配的内存是否够用,可以查看运行完成的job的Counters中(历史服务器),对应的task是否发生过多次GC,以及GC时间占总task运行时间之比。通常,GC时间不应超过task运行时间的10%,即GC time elapsed(ms)/CPU time spent (ms)<10%。
Map需要的内存还需要随着环形缓冲区的调大而对应调整。可以通过如下参数进行调整。
mapreduce.map.memory.mb
Ma需要的CPU核数可以通过如下参数调整
mapreduce.map.cpu.vcores
可以看到内存默认是1G,CPU默认是1核。
如果集群资源充足建议调整:
mapreduce.map.memory.mb=3G(默认1G) 给的高, 降低GC占比
mapreduce.map.cpu.vcores=1(默认也是1)
环形缓冲区
Map方法执行后首先把数据写入环形缓冲区,为什么MR框架选择先写内存而不是直接写磁盘?这样的目的主要是为了减少磁盘i/o
- 环形缓冲默认100M(mapreduce.task.io.sort.mb),当到达80%(mapreduce.map.sort.spill.percent)时就会溢写磁盘。
- 每达到80%都会重写溢写到一个新的文件。
当集群内存资源充足,考虑增大mapreduce.task.io.sort.mb提高溢写的效率,而且会减少中间结果的文件数量。
建议:
- 调整mapreduce.task.io.sort.mb=512M。
- 当文件溢写完后,会对这些文件进行合并,默认每次合并10(mapreduce.task.io.sort.factor)个溢写的文件,建议调整mapreduce.task.io.sort.factor=64。这样可以提高合并的并行度,减少合并的次数,降低对磁盘操作的次数。
2、Combiner
在Map阶段,有一个可选过程,将同一个key值的中间结果合并,叫做Combiner。(一般将reduce类设置为combiner即可)
通过Combine,一般情况下可以显著减少Map输出的中间结果,从而减少shuffle过程的网络带宽占用。
建议:
不影响最终结果的情况下,加上Combiner!!
2.2 Copy阶段
- 对Map的中间结果进行压缩,当数据量大时,会显著减少网络传输的数据量,
- 但是也因为多了压缩和解压,带来了更多的CPU消耗。因此需要做好权衡。当任务属于网络瓶颈类型时,压缩Map中间结果效果明显。
- 在实际经验中Hadoop的运行的瓶颈一般都是IO而不是CPU,压缩一般可以10倍的减少IO操作
2.3 Reduce阶段
1、Reduce资源
每个Reduce资源
mapreduce.reduce.memory.mb=5G(默认1G) 因为reduce是聚合阶段, 数据量比map大
mapreduce.reduce.cpu.vcores=1(默认为1)。
2、Copy
ReduceTask在copy的过程中默认使用 5(mapreduce.reduce.shuffle.parallelcopies参数控制)个并行度进行复制数据。
该值在实际服务器上比较小,建议调整为50-100.
3、溢写归并
Copy过来的数据会先放入内存缓冲区中,然后当使用内存达到一定量的时候spill磁盘。这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置。这个内存大小的控制是通过mapreduce.reduce.shuffle.input.buffer.percent(default 0.7)控制的。
shuffle在reduce内存中的数据最多使用内存量为:0.7 × maxHeap of reduce task,内存到磁盘merge的启动可以通过mapreduce.reduce.shuffle.merge.percent(default0.66)配置。
copy完成后,reduce进入归并排序阶段,合并因子默认为10(mapreduce.task.io.sort.factor参数控制),如果map输出很多,则需要合并很多次,所以可以提高此参数来减少合并次数。
mapreduce.reduce.shuffle.parallelcopies # 复制数据的并行度,默认5;建议调整为50-100
mapreduce.task.io.sort.factor # 一次合并文件个数,默认10,建议调整为64
mapreduce.reduce.shuffle.input.buffer.percent # 在shuffle的复制阶段,分配给Reduce输出缓冲区占堆内存的百分比,默认0.7
mapreduce.reduce.shuffle.merge.percent # Reduce输出缓冲区的阈值,用于启动合并输出和磁盘溢写的过程
第三节 Job调优
1、推测执行
集群规模很大时(几百上千台节点的集群),个别机器出现软硬件故障的概率就变大了,并且会因此延长整个任务的执行时间推测执行通过将一个task分给多台机器跑,取先运行完的那个,会很好的解决这个问题。对于小集群,可以将这个功能关闭。
建议:
- 大型集群建议开启,小集群建议关闭!
- 集群的推测执行都是关闭的。在需要推测执行的作业执行的时候开启
2、Slow Start
MapReduce的AM在申请资源的时候,会一次性申请所有的Map资源,延后申请reduce的资源,这样就能达到先执行完大部分Map再执行Reduce的目的。
mapreduce.job.reduce.slowstart.completedmaps
当多少占比的Map执行完后开始执行Reduce。默认5%的Map跑完后开始起Reduce。
如果想要Map完全结束后执行Reduce调整该值为1
3 、小文件优化
- HDFS:hadoop的存储每个文件都会在NameNode上记录元数据,如果同样大小的文件,文件很小的话,就会产生很多文件,造成NameNode的压力。
- MR:Mapreduce中一个map默认处理一个分片或者一个小文件,如果map的启动时间都比数据处理的时间还要长,那么就会造成性能低,而且在map端溢写磁盘的时候每一个map最终会产生reduce数量个数的中间结果,如果map数量特别多,就会造成临时文件很多,而且在reduce拉取数据的时候增加磁盘的IO。
如何处理小文件?
- 从源头解决,尽量在HDFS上不存储小文件,也就是数据上传HDFS的时候就合并小文件
- 通过运行MR程序合并HDFS上已经存在的小文件
- MR计算的时候可以使用CombineTextInputFormat来降低MapTask并行度
4 、数据倾斜
MR是一个并行处理的任务,整个Job花费的时间是作业中所有Task最慢的那个了。
为什么会这样呢?为什么会有的Task快有的Task慢?
- 数据倾斜,每个Reduce处理的数据量不是同一个级别的,所有数据量少的Task已经跑完了,数据量大的Task则需要更多时间。
- 有可能就是某些作业所在的NodeManager有问题或者container有问题,导致作业执行缓慢。
为什么会产生数据倾斜?
数据本身就不平衡,所以在默认的hashpartition时造成分区数据不一致问题
如何解决?
- 默认的是hash算法进行分区,我们可以尝试自定义分区,修改分区实现逻辑,结合业务特点,使得每个分区数据基本平衡
- 可以尝试修改分区的键,让其符合hash分区,并且使得最后的分区平衡,比如在key前加随机数n-key。
- 抽取导致倾斜的key对应的数据单独处理。
如果不是数据倾斜带来的问题,而是节点服务有问题造成某些map和reduce执行缓慢呢?
使用推测执行找个其他的节点重启一样的任务竞争,谁快谁为准。推测执行时以空间换时间的优化。会带来集群资源的浪费,会给集群增加压力。
第四节 YARN调优
1、NM配置
可用内存
- 刨除分配给操作系统、其他服务的内存外,剩余的资源应尽量分配给YARN。
- 默认情况下,Map或Reduce container会使用1个虚拟CPU内核和1024MB内存,ApplicationMaster使用1536MB内存。
yarn.nodemanager.resource.memory-mb 默认是8192
CPU虚拟核数
- 建议将此配置设定在逻辑核数的1.5~2倍之间。如果CPU的计算能力要求不高,可以配置为2倍的逻辑CPU。
yarn.nodemanager.resource.cpu-vcores
该节点上YARN可使用的虚拟CPU个数,默认是8。
目前推荐将该值设值为逻辑CPU核数的1.5~2倍之间
2、Container启动模式
YARN的NodeManager提供2种Container的启动模式。
yarn.nodemanager.container-executor.class
- 设置为“org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor”,则每次启动container将会启动一个线程来实现资源本地化。该模式下,启动时间较短,但无法做到资源(CPU、内存)隔离。
- 生产模式建议设置为“org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor” ,则每次启动container都会启动一个JVM进程来实现资源本地化。该模式下,启动时间较长,但可以提供较好的资源(CPU、内存)隔离能力。
3、AM调优
运行的一个大任务,map总数达到了上万的规模,任务失败,发现是ApplicationMaster(以下简称AM)反应缓慢,最终超时失败。
失败原因是Task数量变多时,AM管理的对象也线性增长,因此就需要更多的内存来管理。AM默认分配的内存大小是1.5GB。
建议:
任务数量多时增大AM内存
yarn.app.mapreduce.am.resource.mb
第五节 Namenode Full GC
JVM堆内存
- JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
- 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
- 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
- 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
补充:
JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
1、对象分代
- 新生成的对象首先放到年轻代Eden区,
- 当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,
- Survivor0区满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空。
- 经过多次Minor GC仍然存活的对象移动到老年代。
- 老年代存储长期存活的对象,占满时会触发Major GC(Full GC),GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
Minor GC : 清理年轻代 Major GC(Full GC) : 清理老年代,清理整个堆空间,会停止应用所有线程。
2、Jstat
查看当前jvm内存使用以及垃圾回收情况
jstat -gc -t 58563 1s #显示pid是58563的垃圾回收堆的行为统计
Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 9751.8 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354 9752.8 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354 9753.8 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354 9754.8 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354 9755.8 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354 9756.9 12288.0 12288.0 0.0 0.0 158208.0 8783.6 54272.0 23264.6 35496.0 34743.9 4144.0 3931.8 9 0.231 2 0.123 0.354
结果解释:
#C即Capacity 总容量,U即Used 已使用的容量
S0C: 当前survivor0区容量(kB)。
S1C: 当前survivor1区容量(kB)。
S0U: survivor0区已使用的容量(KB)
S1U: survivor1区已使用的容量(KB)
EC: Eden区的总容量(KB)
EU: 当前Eden区已使用的容量(KB)
OC: Old空间容量(kB)。
OU: Old区已使用的容量(KB)
MC: Metaspace空间容量(KB)
MU: Metacspace使用量(KB)
CCSC: 压缩类空间容量(kB)。
CCSU: 压缩类空间使用(kB)。
YGC: 新生代垃圾回收次数
YGCT: 新生代垃圾回收时间
FGC: 老年代 full GC垃圾回收次数
FGCT: 老年代垃圾回收时间
GCT: 垃圾回收总消耗时间
开启HDFS GC详细日志输出
编辑hadoop-env.sh
export HADOOP_LOG_DIR=/hadoop/logs/
增加JMX配置打印详细GC信息
指定一个日志输出目录;注释掉之前的ops
增加新的打印配置
#JMX配置
export HADOOP_JMX_OPTS="-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false"
export HADOOP_NAMENODE_OPTS="-Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:
-INFO,RFAS}
-Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender} $HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS $HADOOP_DATANODE_OPTS"export NAMENODE_OPTS="-verbose:gc -XX:+PrintGCDetails
-Xloggc:${HADOOP_LOG_DIR}/logs/hadoop-gc.log \
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime \
-server -Xms150g -Xmx150g -Xmn20g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 \
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled \
-XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \
-XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m
-XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof \
"export DATENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -Xloggc:${HADOOP_LOG_DIR}/hadoop gc.log \
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime \
-server -Xms15g -Xmx15g -Xmn4g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 \
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled \
-XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \
-XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m
-XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof \
"
export HADOOP_NAMENODE_OPTS="$NAMENODE_OPTS $HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="$DATENODE_OPTS $HADOOP_DATANODE_OPTS"
- -Xms150g -Xmx150g :堆内存大小最大和最小都是150g
- -Xmn20g :新生代大小为20g,等于eden+2*survivor,意味着老年代为150-20=130g。
- -XX:SurvivorRatio=8 :Eden和Survivor的大小比值为8,意味着两个Survivor区和一个Eden区的比值为2:8,一个Survivor占整个年轻代的1/10
- -XX:ParallelGCThreads=10 :设置ParNew GC的线程并行数,默认为 8 + (Runtime.availableProcessors - 8) * 5/8 ,24核机器为18。
- -XX:MaxTenuringThreshold=15 :设置对象在年轻代的最大年龄,超过这个年龄则会晋升到老年代
- -XX:+UseParNewGC :设置新生代使用Parallel New GC
- -XX:+UseConcMarkSweepGC :设置老年代使用CMS GC,当此项设置时候自动设置新生代为ParNew GC
- -XX:CMSInitiatingOccupancyFraction=70 :
老年代第一次占用达到该百分比时候,就会引发CMS的第一次垃圾回收周期。后继CMS GC由HotSpot自动优化计算得到。
3、GC 日志解析
jstat命令输出
ime S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
5.7 34048.0 34048.0 0.0 34048.0 272640.0 194699.7 1756416.0 181419.9 18304.0 17865.1 2688.0 2497.6 3 0.275 0 0.000 0.275
6.7 34048.0 34048.0 34048.0 0.0 272640.0 247555.4 1756416.0 263447.9 18816.0 18123.3 2688.0 2523.1 4 0.359 0 0.000 0.359
7.7 34048.0 34048.0 0.0 34048.0 272640.0 257729.3 1756416.0 345109.8 19072.0 18396.6 2688.0 2550.3 5 0.451 0 0.000 0.451
8.7 34048.0 34048.0 34048.0 34048.0 272640.0 272640.0 1756416.0 444982.5 19456.0 18681.3 2816.0 2575.8 7 0.550 0 0.000 0.550
9.7 34048.0 34048.0 34046.7 0.0 272640.0 16777.0 1756416.0 587906.3 20096.0 19235.1 2944.0 2631.8 8 0.720 0 0.000 0.720
10.7 34048.0 34048.0 0.0 34046.2 272640.0 80171.6 1756416.0 664913.4 20352.0 19495.9 2944.0 2657.4 9 0.810 0 0.000 0.810
11.7 34048.0 34048.0 34048.0 0.0 272640.0 129480.8 1756416.0 745100.2 20608.0 19704.5 2944.0 2678.4 10 0.896 0 0.000 0.896
12.7 34048.0 34048.0 0.0 34046.6 272640.0 164070.7 1756416.0 822073.7 20992.0 19937.1 3072.0 2702.8 11 0.978 0 0.000 0.978
13.7 34048.0 34048.0 34048.0 0.0 272640.0 211949.9 1756416.0 897364.4 21248.0 20179.6 3072.0 2728.1 12 1.087 1 0.004 1.091
14.7 34048.0 34048.0 0.0 34047.1 272640.0 245801.5 1756416.0 597362.6 21504.0 20390.6 3072.0 2750.3 13 1.183 2 0.050 1.233
15.7 34048.0 34048.0 0.0 34048.0 272640.0 21474.1 1756416.0 757347.0 22012.0 20792.0 3200.0 2791.0 15 1.336 2 0.050 1.386
16.7 34048.0 34048.0 34047.0 0.0 272640.0 48378.0 1756416.0 838594.4 22268.0 21003.5 3200.0 2813.2 16 1.433 2 0.050 1.484
查看GC日志输出
3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K), 0.0844702 secs]
272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23 sys=0.03, real=0.09 secs]
4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K), 0.1013723 secs]
342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25 sys=0.05, real=0.10 secs]
... cut for brevity ...
11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K), 0.0857219 secs]
971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26 sys=0.04, real=0.09 secs]
12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K), 0.0821774 secs]
1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25 sys=0.03, real=0.08 secs]
12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K), 0.1086667 secs]
1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24 sys=0.04, real=0.11 secs]
13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)] 936667K(2063104K),
0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
13.102: [CMS-concurrent-mark-start]
13.341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01, real=0.24 secs]
13.341: [CMS-concurrent-preclean-start]
13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
13.350: [CMS-concurrent-abortable-preclean-start]
13.878: [GC (Allocation Failure) [ParNew: 16844397K->85085K(18874368K), 0.0960456 secs]
116885867K->100127390K(155189248K), 0.0961542 secs] [Times: user=0.14 sys=0.00, real=0.05 secs]
14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22 sys=0.07,
real=1.01 secs]
14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366: [Rescan (parallel) ,
0.0291598 secs]14.395: [weak refs processing, 0.0000232 secs]14.395: [class unloading, 0.0117661
secs]14.407: [scrub symbol table, 0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1
CMS-remark: 976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14 sys=0.00,
real=0.05 secs]
14.412: [CMS-concurrent-sweep-start]
14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00, real=0.22 secs]
14.633: [CMS-concurrent-reset-start]
14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
ParNew: 16844397K->85085K(18874368K), 0.0960456 secs
- 其中, 16844397K 表示GC前的新生代占用量, 85085K 表示GC后的新生代占用量,GC后Eden和一个Survivor为空,所以 85085K 也是另一个Survivor的占用量。括号中的 18874368K 是Eden+一个被占用Survivor的总和(18g)。
116885867K->100127390K(155189248K), 0.0961542 secs
- 其中,分别是Java堆在垃圾回收前后的大小,和Java堆大小。说明堆使用为116885867K=111.47g,回收大小为100127390K=95.49g,堆大小为155189248K=148g(去掉其中一个Survivor),回收了16g空间.
总结:
在HDFS Namenode内存中的对象大都是文件,目录和blocks,这些数据只要不被程序或者数据的拥有者人为的删除,就会在Namenode的运 行生命期内一直存在,所以这些对象通常是存在在old区中,所以,如果整个hdfs文件和目录数多,blocks数也多,内存数据也会很大
如何降低Full GC的影响?
- 计算NN所需的内存大小,合理配置JVM
- 使用低卡顿G1收集器
为什么会有G1呢?
因为并发、并行和CMS垃圾收集器都有2个共同的问题:
- 老年代收集器大部分操作都必须扫描整个老年代空间(标记,清除和压缩)。这就导致了GC随着Java堆空间而线性增加或减少
- 年轻代和老年代是独立的连续内存块,所以要先决定年轻代和年老代放在虚拟地址空间的位置
G1垃圾收集器利用分而治之的思想将堆进行分区,划分为一个个的区域。
G1垃圾收集器将堆拆成一系列的分区,这样的话,大部分的垃圾收集操作就只在一个分区内执行,从而避免很多GC操作在整个Java堆或者整个年轻代进行。
编辑hadoop-env.sh
export HADOOP_NAMENODE_OPTS="-server -Xmx220G -Xms200G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -
XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:-ResizePLAB -
XX:+PerfDisableSharedMem -XX:-OmitStackTraceInFastThrow -XX:G1NewSizePercent=2 -
XX:ParallelGCThreads=23 -XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=32M -
XX:G1HeapWastePercent=10 -XX:G1MixedGCCountTarget=16 -verbose:gc -XX:+PrintGCDetails -
XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=100M -Xloggc:/var/log/hbase/gc.log -
Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,RFAS} -
Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender} $HADOOP_NAMENODE_OPTS"
注意:如果现在采用的垃圾收集器没有问题,就不要选择G1,如果追求低停顿,可以尝试使用G1
第六节 Hadoop二次开发环境搭建
系统环境
系统: CentOS-7_x86_64
protobuf: protoc-2.5.0
maven: maven-3.6.0
hadoop: hadoop-2.9.2
java: jdk1.8.0_131
cmake: cmake-2.8.12.2
OpenSSL: OpenSSL 1.0.2k-fips
findbugs: findbugs-1.3.9
准备工作
# 安装编译需要的依赖库
yum install -y lzo-devel zlib-devel autoconf automake libtool cmake openssl-devel cmake gcc gcc-c++
安装Maven
#上传maven安装包
# 解压缩
$ tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local/
# 配置到系统环境变量
$ vim /etc/profile
export MAVEN_HOME=/usr/local/apache-maven-3.6.3
export PATH=$PATH:$MAVEN_HOME/bin
# 刷新配置文件
$ source /etc/profile
# 验证maven安装是是否成功
$ mvn -version
[root@localhost ~]# mvn -versionApache Maven 3.6.0 (ff8f5e7444045639af65f6095c62210b5713f426; 2018-10-25T03:39:06+08:00)
Maven home: /usr/local/apache-maven-3.6.3
Java version: 1.8.0_131, vendor: Oracle Corporation
Java home: /usr/local/jdk1.8.0_131/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.20.13-1.el7.elrepo.x86_64", arch: "amd64", family: "unix"
安装protobuf
# 安装依赖环境
$ yum groupinstall Development tools -y
# 下载
$ https://github.com/protocolbuffers/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.gz
#上传protobuf安装包
# 解压缩
$ tar -zxvf protobuf-2.5.0.tar.gz
cd protobuf-2.5.0
# 进入解压目录 配置安装路径(--prefix=/usr/local/protobuf-2.5.0)
$ ./configure --prefix=/usr/local/protobuf-2.5.0
# 编译
$ make
# 验证编译文件
$ make check
# 安装
$ make install
# 配置protobuf环境变量
$ vim /etc/profile
export PROTOCBUF_HOME=/usr/local/protobuf-2.5.0
export PATH=$PATH:$PROTOCBUF_HOME/bin
# 刷新配置文件
$ source /etc/profile
# 验证是否安装成功
$ protoc --version
[root@localhost ~]# protoc --version
libprotoc 2.5.0
安装Findbugs
#下载
$ https://jaist.dl.sourceforge.net/project/findbugs/findbugs/1.3.9/findbugs-1.3.9.tar.gz
#上传安装包
# 解压缩
$ tar -zxvf findbugs-1.3.9.tar.gz -C /usr/local/
# 配置系统环境变量
$ vim /etc/profile
export FINDBUGS_HOME=/usr/local/findbugs-1.3.9export PATH=$PATH:$FINDBUGS_HOME/bin
# 刷新配置文件
$ source /etc/profile
# 验证是否安装成功
$ findbugs -version
[root@localhost ~]# findbugs -version
1.3.9
添加aliyun镜像
找到maven环境下的settings.xml文件,添加镜像代理
<mirror><id>nexus</id><mirrorOf>*</mirrorOf><url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror><mirror><id>nexus-public-snapshots</id><mirrorOf>public-snapshots</mirrorOf><url>http://maven.aliyun.com/nexus/content/repositories/snapshots/</url>
</mirror>
上传源码文件
进入代码文件目标路径
/root/hadoop-2.9.2-src/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client- core/src/main/java/org/apache/hadoop/mapreduce/lib/input
编译
进入Hadoop源码目录
cd /root/hadoop-2.9.2-src
执行编译命令
mvn package -Pdist,native -DskipTests -Dtar
问题解决
hadoop-aws:jar时缺少依赖包DynamoDBLocal:jar
选择手动下载该Jar包,上传到本地maven仓库
cd /root/.m2/repository/com/amazonaws/DynamoDBLocal/1.11.86
编译成功
这篇关于3.1.4 Hadoop, Yarn, 资源调度, AH源码分析, 3.x 新特性概述, Job三原则, 调优(Shuffle, Job, YARN, NN Full GC), 二次开发环境搭建的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!