随着国内互联网行业的倒退,万亿规模的超大集群尽管已不像几年前那么百里挑一,然而也并不多见,尤其是波及超万亿规模的超大集群性能故障排查的机会就更加稀少。

而这次我所进行的超万亿规模的Hadoop NameNode性能故障排查也是在我守业几年以来所遇到的集群规模最大,耗时最长,排查工作量最大,头发掉的最多,最终都不得不求助于大牛的一次经验。

因而在问题解决之后,我也第一工夫将这次的整个排查过程记录下来进行总结,心愿能给看到的各位同学有所帮忙。上面,enjoy:


起 因

事件的起因是因为近日客户反馈应用咱们数据库的成果忽然变差,之前秒级响应的数据查问与检索,当初却总是在“转圈”,卡住不动了。因为是忽然产生的景象,曾经在现场的同当时排除了业务变动,然而并未发现问题。作为本人创建公司后第一个接到的万亿数据体量的大我的项目,我本身也非常重视,马上第一工夫奔赴现场。

这里先容介绍一下该平台架构,底层采纳hadoop进行分布式存储,两头数据库采纳的录信LSQL,数据实时导入采纳kafka进行。每天的数据规模是500亿,数据存储周期为90天,一共有4000多张数据表,其中最大的单表数据规模近2万亿条记录,总数据规模将近5万亿,存储空间占8PB。

数据平台撑持的根本应用次要包含数据的全文检索、多维查问,以及地理位置检索、数据碰撞等操作。也会有局部业务波及数据的统计和剖析,会有极少量的数据导出与多表关联操作。


经 过

Before the day:初步定位问题

话说我在守业之前在腾讯做Hermes零碎,每日接入的实时数据量就曾经达到了3600亿/天,之后更是达到了每日近万亿条数据的实时导入。为了不显得那么凡尔赛,我只想说,我和梁启超在北大演讲时的情绪一样:我对超大集群没什么理解,然而还是有那么一点喽!面对以后每日500-1000亿规模的零碎,我在思考是不是买票的时候把当天的回程票也买了......

为了疾速定位问题,还没登程之前我就跟现场要了一些日志和jstack,初步定位是hadoop NameNode的瓶颈,而NN的优化咱们此前也做了很屡次,别无其他,唯手熟尔。

下图为过后堆栈的剖析状况,预计在座诸位看了都会信念满满,这很显著就是hadoop卡顿。

②First Day:尝试调整log4j

到现场的第一天,仍然是风和日丽,情绪持续放弃漂亮。

我到现场后第一件事件就是一直的抓hadoop Namenode的堆栈Jstack。从中失去的论断是问题的确是卡顿在NN上。此处NN是一个全局锁,所有的读写操作都在排序期待,详情如下图所示:

1. 卡在哪里

这个锁的期待个数居然长达1000多个,不卡才怪呢,咱们再细看一下,以后领有这个锁的线程在做什么?

2. 问题剖析

很显著,在记录log上存在瓶颈,阻塞的工夫太久。

1) 记录的log4j不应该加【%L】,它会创立Throwable对象,而这个在java里是一个重对象。

2) 日志记录太频繁,刷盘刷不动。

3) log4j有全局锁,会影响吞吐量。

3. 调整计划

1) 客户的hadoop版本采纳的是2.6.0版本,该版本的hadoop,在日志解决上存在诸多问题,故咱们将官网明确示意存在问题的patch打了进来

https://issues.apache.org/jir... 因日志起因导致nn慢

https://issues.apache.org/jir... 将日志记录到锁外,防止卡锁

https://issues.apache.org/jir... processIncrementalBlockReport 导致的记录日志问题,重大影响NN性能

2) 禁用namenode所有info级别的日志

察看发现当有大量日志输入的时候,全局锁会阻塞NN。

目前批改形式是屏蔽到log4j的日志输入,禁用namenode所有info级别的日志。

3) log4j 的日志输入去掉【%L】参数

这个参数会为了失去行号而创立new Throwable对象,这个对象对性能影响很大,大量创立会影响吞吐量。

4) 启用异步审计日志

dfs.namenode.audit.log.async 设置为true,将审计日志改为异步。

4. 优化成果

优化之后,的确因log4j导致的卡顿问题不存在了,但hadoop的吞吐量仍然卡,仍旧卡在lock上。

③Second Day:优化du,排查解决所有卡顿

接着昨天的工作:

1. 在解决了log4j的问题后,持续抓jstack,抓到如下地位:

2. 通过代码进行剖析,发现的确此处有锁,证实此处会引起所有拜访阻塞:

3. 持续深刻研读代码,发现受如下参数管制:

(2.6.5版本这个默认值是5000,曾经不存在这个问题了)

这个参数的外围逻辑是,如果配置上大于零的值,它会距离肯定文件数量,开释锁,让别的程序得以继续执行,该问题只会在hadoop2.6.0的版本里存在,之后的版本里曾经对此做了修复。

4. 解决办法

1) 打上官网patch:

https://issues.apache.org/jir...

2) lsql外部移除所有对于hadoop du的应用

5. 为什么要打patch

2.6.5版本中,能够本人定义休眠工夫,默认休眠工夫为500ms,而2.6.0休眠工夫为1ms,我放心太短,会呈现问题。

持续依照原先思路,排查所有的jstack 。将所有波及卡顿的中央都一一解决掉,至此hadoop通过jstack曾经抓不到任何的流动线程,然而仍然卡顿在读写锁的切换上,这阐明:

1.namenode外部的每个函数曾经最优,jstack根本抓不到了;

2.堆栈调用只能看到近1000个读写锁在一直切换,阐明nn的申请并发十分高,多线程之间锁的上下文切换曾经成为了次要瓶颈。

所以当下次要思路应该落在如何缩小NN的调用频率下面。

④Third Day:尽可能减少NN申请频率

为了缩小NN的申请频率,尝试了多个办法:

1. 启用录信数据库lsql的不同表不同分片性能

思考到现场有4000多张表,每张表有1000多个并发写入分片,有可能是同时写入的文件数太多,导致的nn申请频率太高,故思考将那些小表,进行分片合并,写入的文件数量少了,申请频率自然而然就升高了。

2. 与现场人员配合,清理不必要的数据,缩小hadoop集群的压力。清理后hadoop集群的文件块数由将近2亿,升高到1.3亿,清理力度足够大。

3. 调整一系列与NN无关交互的心跳的频率:如blockmanager等相干参数。

4. 调整NN外部锁的类型:由偏心锁调整为非偏心锁。

本次调整波及的参数有:

  • dfs.blockreport.intervalMsec 由21600000L调整为259200000L (3天),全量心跳
  • dfs.blockreport.incremental.intervalMsec 增量数据心跳由0改为300,尽量批量一次上报 (老版本无该参数)
  • dfs.namenode.replication.interval 由3秒调整为60秒,缩小心跳频率
  • dfs.heartbeat.interval 心跳工夫由默认3秒调整为60秒,缩小心跳频率
  • dfs.namenode.invalidate.work.pct.per.iteration 由0.32调整为0.15 (15%个节点),缩小扫描节点数量

本次调整波及的堆栈:

最终后果卡顿问题仍然存在。自己曾经江郎才尽,人曾经懵了,不晓得该如何解决。

⑤Fourth Day:机关用尽,思考建设分流机制

拖着曾经间断熬了三个早晨的疲乏身躯,第四天一早就跟公司和客户汇报排查具体情况,也间接说了曾经没有任何的思路。心愿能启用B计划:

1.启用hadoop联邦计划,靠多个namenode解决当下问题;

2.立刻批改录信lsql数据库,在一个lsql数据库内适配hadoop多集群计划,也就是搭建两个齐全一样的集群,录信数据库启动600个过程,300个过程申请旧集群,300个过程分流到新集群,以达到加重压力的目标。

家里(公司)的意见是先回去睡觉,头脑清醒时再做决定。

客户这边倡议持续排查,因为零碎曾经稳固运行一年多了,没道理忽然就不行了,还是心愿深入研究一下。

就像是系统故障大部分一次重启就能解决,我决定先睡会,期待醒了之后问题可能迎刃而解。
睡醒之后,穷途末路的我只好求助于老同事高高,高高是我以前在腾讯时专门负责HDFS的大牛,他对hadoop的精通水平堪比我熟知各类防脱发诀窍,而且上万台大集群的优化教训,可遇而不可求,我想如果他也不能点播一二,恐怕就没人搞得定了,我也不用白费力气。

高高首先询问了集群的根本状况,并给我多项无效倡议。最让我振奋的是依据高高的剖析,咱们的集群相对没有达到性能的下限。

⑥The last day:对调用NN的锁的每个函数进行剖析

这次没有间接看jmx信息,放心后果不精确。采纳的是btrace这个工具,排查具体是哪个线程频繁给NN加锁,导致NN负载如此之高。

破费了3个小时剖析,最终令人惊喜的是发现processIncrementalBlockReport这个线程申请频率十分高,远远高于其余线程。而这个线程不是datanode (dn)节点增量心跳的逻辑吗?为什么频率如此之高?心跳频率我不是都改掉了吗?难道都没失效么?

认真查看hadoop代码,发现这个逻辑的确有问题,每次写数据和删数据都会立刻调用,而我设置的那些心跳参数在客户的这个版本的hadoop集群里并没有这方面优化,设置了也没用,于是紧急在网上寻找patch的办法,最终找到了这个,它不仅仅解决了心跳频率的问题,还解决了加锁频率问题,通过缩小锁的应用次数,从而缩小上下文切换的次数,进而晋升nn的吞吐量。

迅速打上此patch,  显著发现NN吞吐量上来了,而且不仅仅是拜访NN不卡了,实时kafka的生产速度也一下子由原先的每小时解决40亿,回升至每小时解决100亿,入库性能也跟着翻倍。打上patch后,此问题失去了基本的解决。

究其根本原因在于HDFS NameNode外部的繁多锁设计,使得这个锁显得极为的“重”。持有这个锁须要付出的代价很高。每个申请须要拿到这个锁,而后让NN 去解决这个申请,这外面就蕴含了很强烈的锁竞争。因而一旦NN的这个锁被一些大规模的导入/删除操作,容易使NameNode一下子解决大量申请,其它用户的工作会马上受到影响。这次patch的次要作用就是增量汇报的锁批改为异步的锁——让删除、上报等操作不影响查问。

具体详细描述与改法参考这里:

https://blog.csdn.net/android...


总 结

最初,针对于这次性能故障的排查,我从问题成因和解决倡议两个方面总结一下:

①问题成因

零碎之前始终运行安稳,忽然呈现的问题的起因次要是因为以下几个:

1.用户删除了大量文件,造成hadoop压力增大

  • 近期硬盘快要满了,集中清理了一批数据
  • 最近hadoop不稳固,集中开释了一大批文件

2. 近期显著的日常数据量暴增对hadoop调优后,重入数据,按日志进行数据条数统计,最近的数据规模减少很多

3.生产数据积压

本次调优过程中,因为数据积压了很多天,导致kafka始终在满速生产数据。而在满速生产的状况下,会对nn造成较大的冲击。

4.快照和mover对hadoop造成的冲击

  • 清理快照的时候,开释了大量的数据块,造成数据的删除
  • mover新增了大量的数据块,以致零碎删除了大量的ssd上的文件块。且因节点数增多,心跳频繁,刹时都进行processIncrementalBlockReport对nn造成较大的压力

②我的几点倡议

1.Never give up easily!

在排查的第四天,在尝试过多种解决方案之后,我也想过要放弃,并且认为这次的性能故障是无解的。在这种时候咱们无妨多与共事,哪怕是以前的共事领导探讨探讨,兴许会带来不一样的思路和启发,要置信群体的智慧!

2.肯定要理解的hadoop原理,这也是本次hadoop调优的关键点

(1) 当咱们在HDFS中删除文件时:namenode只是把目录入口删掉,而后把须要删除的数据块记录到pending deletion blocks列表。当下一次datanode向namenode发送心跳时,namenode再把删除命令和这个列表发送到datanode端,所以这个pending deletion blocks列表很长很长,导致了timeout。

(2)当咱们导入数据时:客户端会将数据写入到datanode里,而datanode在接到数据块后,会立刻调processIncrementalBlockReport给NN汇报,写入数据量越多,越频繁,机器数量越多,过程越多,调用NN就会越频繁。所以本次的异步锁patch,在这里才会有成果。

3.最要害的一点:千万不要应用hadoop2.6.0这个版本!!!

用hadoop官网的话来讲,别的版本都是存在a few of bug,而这个版本存在a lot of bug,所以回去后第一件事要督促客户尽快降级换版本。