此文章仅代表个人简介,根据个人亲身经历感受所得,希望给更多同行参考,入行有风险,且行且珍惜,哈哈哈,开个玩笑。
很多人都知道我们这个行业近些年都比较赚钱,工资相对较高,我只能说你们并没有看见我们的付出、努力和艰辛,各行各业都是这样,我相信。
作为一个多年经验的资深程序猿,自14年实习开始就进入该行业,从一名程序小白开始,最早做java开发,一步一步,做到中级,再到高级,终于有一天,我发现整天搞这些一点意思都没有,每天还得不断对自己进行技术更新,学习、底层原理、源码剖析等等,窝内个天呀,快结束我吧,没意思、太没意思了;就这样我自己想着转变,刚好大环境下,大数据逐渐成熟,国内需求越来越多,往大数据方向靠靠吧(PS:其实工资诱人 ),就这样我就考虑,我是自学?从公司转型(遗憾,当时公司没有大数据需求)?找个培训班学习?在自己万般纠结中,我毅然决然的选择了裸辞去找家培训机构学习,我现在想来,我也挺佩服我自己的,不过还好,我有个对我从来都是无条件支持的伟大母亲,她信我。这也算是我的一大动力吧,在此,借助这篇文章,默默的对我的母亲表达感谢,直到如今,她都一直对我的决定非常的支持。就这样,我努力学习完六个月,出去找工作,确实,很顺利,薪资也大幅度增长,这些年做大数据,同样我遇到很多问题,各种瓶劲。我不知道为什么,我有一次陷入了盲目,不知道方向在哪,大数据技术很多,我不停的学,不停的报班,不停的总结,不停的看各种书籍、博客、公众号,我突然感觉我好无知,我简直是个垃圾,我不知道该怎么办?我连总结自己的勇气都没有了,我明明很努力了,可为什么还是会这样?
终于我忍不住和我母亲沟通交流,母亲是位教师,他很懂我,也很会劝导我(细节就不细说了,道理大家都懂),终于我决定重新认识自己,对我自己的经历做出总结。
- 我觉得我虽然是在做大数据,但是都太过于浅显,并没有好的经历,好的项目,总结起来真正的经验不足。
- 认知不够,因为项目局限性,很多公司都是简单浅显做一些基础数仓,简单计算一些指标就可以了,所以自己认知停留,在大数据体系上面认知不够,比如:怎么做一个全域数仓?怎么构建指标体系?怎么做数据治理?怎么做数据管理?怎么做数据平台?怎么做数据中台?等等。。
- 很多时候,需要停下脚步,巩固复习,贪多嚼不烂,找准一个方向,一个内容抓实学,必有成长。
就在这个时候,说来也很巧合,抓住自己的弱势,好的项目经验不足,不能充分体现大数据场景以及优势和作用。我被一个培训的大数据内容所吸引(我自己是做大数据的,仿佛自己被大数据杀熟了 ),同时我也第一次感觉大数据杀熟也挺不错的。为什么呢?这个其实就是拉钩教育的大数据高薪训练营,我为什么着重说下这个呢,其实真正吸引我的是他们的几个项目,循序渐进,而且做到了真正的大项目,也非常全面。
-
真正达到了PB级,这个是我们在做很多大数据项目中遇不到的,这也是我现在的窘迫,从来没有遇到过那个公司哪个项目达到PB级别,没有真正的大数据,很多场景、问题是遇不到的,解决问题的局限性就来了。 -
项目全面,我仔细看了项目简介,而且我当时报班时也很犹豫害怕,怕项目深度不够,专门还咨询了授课讲师,期间我们也交流了很多,中肯的建议很受用。就拿第一个离线数仓来说,数量级别达到PB级,而且满足现在市场定位,很全面,不简简单单就只是数仓相关的内容,除了指标体系、数据建模、数据计算等之外的还有埋点数据采集、元数据管理、数据质量监控、即席查询等,这个是很多培训机构做不到的。我真的很喜欢这个。话不多少,没图说个** -
项目种类多,而且很多企业都遇不到的,智慧物流大数据项目、企业级电商实时数仓项目、人才职位画像匹配推荐。项目种类很全,而且循序渐进的进行,保证了质量
看到了没,做了这么多年大数据,我真的没实际做过这样好的大型项目(惭愧惭愧),就像和别人说的,我只是缺乏一个机会,前提是你要时刻做好准备,可以有胜任这个机会的能力。
我通过多方咨询、考察、调研,终于做下决定,一个字,“干”就完了;其实我报了很多学习班,真的,因为我总感觉我欠缺太多,我需要不断补充自己,现在想想,其实不用,找好一个学习机会,扎实去学,调研清楚就行,所以通过我个人推荐,这个高薪训练营真的很不错。为啥?你肯定觉得我是卧底,I swear! 我不是,我很中肯的评价,这个为什么好:
- 学习循序渐进,课时是解锁模式,没个课时分为很多节,中间又分了小结作业,用于自测,然后,课时完成会有课时作业,提交后,老师批改。学完一个课时才会解锁下一个课时。我觉得这样很有必要,贪多嚼不烂,就像我上面说的,一步一步的走,每个步骤夯实自己,楼才能盖的更高,虽然前面基础很多我都会,但是我还是按耐着自己的性子,一点点学习,我觉得查缺补漏真的很好,很多以前没注意或者不知道的都被填补了。
- 资料齐全,我觉得这个很重要,有时候我们自己搭建什么集群啥的,有点经验完全自己可以胜任,搭建一次能知道很多为什么,资料写的也很全面,这么有什么问题,不这样做又会遇到什么问题,可以解决很多实际问题。
- 每个课时自己学完,会有直播的方式,老师带着大家一起温故知新,并且拓展知识面。
- 市面上使用的技术栈,训练营都会有讲解,而且非常详细,最关键的是关联项目,有实践的经验。
- 笔记,这个真的是,不用说,很重要,像我们大数据技术栈这么多,学习的内容实在是无法用语言表达(高薪-存在即合理),每次提交课时作业时,都会附上自己的学习笔记链接,讲师会核查纠正。
- 大厂内推的机会,国内屈指可数的一流招聘网站,给你开玩笑呢?为啥要进大厂,还不是工资高?其实也不全是,在我看来,更多的是因为大厂有实力和机会做大项目,对于我们研发人员来说成长很大。
说实话,我挺感谢有这样的经历,让我知道我需要什么,我的目标是什么。也祝愿大家可以找到自己满意的工作。 - 讲师基本都是大厂出来的,很多都是大厂在职的,身经百战,经验丰富。说不定看中你,直接给你拉进大厂了,哈哈哈
- 还有很多辅助你学习的老师、班主任等,除了学习,其它需要帮助的都随时帮助您
因为我才刚学不久(为什么不早点让我遇到这个学习机会 ),附一篇我的学习笔记,大家可以参考:
Hadoop学习笔记
1. MapReduce
Reduce端join缺点:
- 数据聚合功能在reduce端完成,reduce端并行度一般不高,所以执行效率存在隐患
- 相同key的数据去往同一个分区,如果数据本身存在不平衡,会造成数据倾斜
join的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载 则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜。
解决方案:map端join实现方式
Map端join:
- 适用于关联表中有小表的情形;
- 避免了reduce端数据倾斜的问题
可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果,可以大大提高join操作的并行度,加快处理速度
mr程序中有大量小文件如何处理:
- 使用combineInputformat,跳出原先TextInputformat的切片逻辑(一个文件切一个分片,一个分片对应一个mapTask,也就是一个小文件对应一个MapTask),使用combineInputformat,可以将多个小文件合并成一个逻辑分片,由一个mapTask来完成,可以解决mapreduce中一个小文件对应一个mapTask造成资源浪费的问题,这个只是解决在mapreduce层面的问题。但是,这些数据存储到HDFS上,对于NameNode来说,内存依然存在浪费。
- 自定义InputFormat合并成一个大文件,可以解决NameNode内存浪费的情况。
MR算法之MergeSort归并排序:
合并
合并细节(降低空间复杂度)
- 不断地将当前序列平均分割成 2个子序列 直到不能再分割(序列中只剩 1个元素)
- 不断地将 2个子序列合并成一个有序序列 直到最终只剩下 1个子序列
时间复杂度:O(nlogn) 空间复杂度:O(n)
QuickSort - 快排
-
时间复杂度
- 最坏情况:
- 最好情况:
-
空间复杂度
- 由于递归调用,每次类似折半效果所以空间复杂度是O(logn)
2. Yarn资源调度
Yarn架构
ResourceManager(rm):处理客户端请求、启动/监控ApplicationMaster、监控NodeManager、资源分配与调度;
NodeManager(nm):单个节点上的资源管理、处理来自ResourceManager的命令、处理来自 ApplicationMaster的命令;
ApplicationMaster(am):数据切分、为应用程序申请资源,并分配给内部任务、任务监控与容错。 Container:对任务运行环境的抽象,封装了CPU、内存等多维资源以及环境变量、启动命令等任务运行相关的信息。
Yarn任务提交(工作机制)
3. Yarn调度策略
FIFO Scheduler
先进先出调度器,按照任务到达时间排序,执行任务
Capacity Scheduler
Apache Hadoop默认调度器 允许多个组织共享整个集群,每个组织获得集群部分计算能力。通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源,这样整个集群就可以通过设置多个队列的方式给多个组织提供服务。队列内部又可以垂直划分,这样一个组织内部的多个成员就可以共享这个队列资源,在一个队列内部资源的调度是采用FIFO策略。
Fair Scheduler
公平调度器,CHD版本的Hadoop默认使用的调度器 设计目标是为所有应用分配公平的资源(公平的定义可通过参数设置),公平调度也可以在多个队列间工作。
举个🌰:
假设有两个用户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
<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>
重启yarn,从界面可以看到
5. MR调优
5.1 Job执行三原则
原则一 充分利用集群资源
Job运行时,尽量让所有的节点都有任务处理,这样能尽量保证集群资源被充分利用,任务的并发度达到最大。可以通过调整处理的数据量大小,以及调整map和reduce个数来实现。
- Reduce个数的控制使用“mapreduce.job.reduces”
- Map个数取决于使用了哪种InputFormat,默认的TextFileInputFormat将根据block的个数来分配map数(一个block一个map)。
原则二 ReduceTask并发调整
努力避免出现以下场景
- 观察Job如果大多数ReduceTask在第一轮运行完后,剩下很少甚至一个ReduceTask刚开始运行。 这种情况下,这个ReduceTask的执行时间将决定了该job的运行时间。可以考虑将reduce个数减少。
- 观察Job的执行情况如果是MapTask运行完成后,只有个别节点有ReduceTask在运行。这时候集 群资源没有得到充分利用,需要增加Reduce的并行度以便每个节点都有任务处理。
原则三 Task执行时间要合理
一个job中,每个MapTask或ReduceTask的执行时间只有几秒钟,这就意味着这个job的大部分时间 都消耗在task的调度和进程启停上了,因此可以考虑增加每个task处理的数据大小。建议一个task处理时间为1分钟。
5.2 shuffle调优
Shuffle阶段是MapReduce性能的关键部分,包括了从MapTaskask将中间数据写到磁盘一直到ReduceTask拷贝数据并最终放到Reduce函数的全部过程。这一块Hadoop提供了大量的调优参数。
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
Map需要的CPU核数可以通过如下参数调整
mapreduce.map.cpu.vcores
内存默认是1G,CPU默认是1核。
如果集群资源充足建议调整:
mapreduce.map.memory.mb=3G(默认1G) 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!!
Copy阶段
- 对Map的中间结果进行压缩,当数据量大时,会显著减少网络传输的数据量
- 但是也因为多了压缩和解压,带来了更多的CPU消耗。因此需要做好权衡。当任务属于网络瓶颈类型时,压缩Map中间结果效果明显。
- 在实际经验中Hadoop的运行的瓶颈一般都是IO而不是CPU,压缩一般可以10倍的减少IO操作
Reduce阶段
1、Reduce资
mapreduce.reduce.memory.mb=5G(默认1G)
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)控制的。
shuffile在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输出缓冲区的阈值,用于启动合并输出和磁盘 溢写的过程
5.3 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执行缓慢呢?
使用推测执行找个其他的节点重启一样的任务竞争,谁快谁为准。推测执行时以空间换时间的优化。
会带来集群资源的浪费,会给集群增加压力。
5.4 YARN调优
1、NM配置
- 可用内存
刨除分配给操作系统、其他服务的内存外,剩余的资源应尽量分配给YARN。 默认情况下,Map或Reduce container会使用1个虚拟CPU内核和1024MB内存, ApplicationMaster使用1536MB内存。
yarn.nodemanager.resource.memory-mb 默认是8192
YARN的NodeManager提供2种Container的启动模式。
默认,YARN为每一个Container启动一个JVM,JVM进程间不能实现资源共享,导致资源本地化的时间开销较大。针对启动时间较长的问题,新增了基于线程资源本地化启动模式,能够有效提升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
5.5 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 2876 1s
结果解释:
#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
增加新的打印配置
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命令输出
查看GC日志输出
- 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
这还只是针对我个人写的笔记,是不是超级详细,也有很多你没接触到的?
技术没有上限,学习永不止步,同样年龄不是问题! 大家加油,一起努力
|