问题描述
在集群运行一段时间后,我们发现一些HDFS上一些解压文件的作业性能下降很明显,同时查看作业日志发现HDFS的读写耗时较长。
原因分析
进一步查看HDFS的日志,发现有大量删除文件的记录,导致文件写入较慢,产生异常,对应的日志如下:
重点看这条记录:
org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.server.namenode.NotReplicatedYetException): Not replicated yet: /tmp/20220905_000XH000.DAT
结合HDFS的日志,具体为Namenode在删除操作上变得非常缓慢,以至于经常阻塞 IBRs(Block Report)并且文件无法正确关闭。在这个作业中,文件被暂存到/tmp目录,该文件时遇到了IBRs阻塞的情况,导致触发重试等待文件被正常关闭。
在HDFS的jira上也有类似的问题,对应的链接如下:
issues.apache.org/jira/browse…
issues.apache.org/jira/browse…
issues.apache.org/jira/browse…
在当前HDFS删除操作对FoldedTreeSet结构的处理上,存在一些瓶颈。FoldedTreeSet是一种数据结构,用于高效地存储和检索键值对。在每次 jstack 采样时,都发现操作主要集中在 FoldedTreeSet 的 removeAndGet 方法上,该方法负责从树集合中删除并获取一个元素。一个线程在执行 FoldedTreeSet 的 removeAndGet 方法时处于运行状态(RUNNABLE)。这个线程似乎在执行删除操作,但出于某种原因变得缓慢。由于问题主要出现在删除操作上,可能的解决方案包括优化删除操作或者改善 FoldedTreeSet 数据结构的效率,以减少删除操作的时间。此外,可以考虑升级 Hadoop 和 HDFS 的版本,因为有些问题可能是由于当前的HDFS版本存在的已知bug导致的,升级到更新的版本可能会解决问题。
解决办法
- 长期方案
考虑升级新版本的HDFS,新版本优化了HDFS中清理文件的机制,采用异步的方式执行nn指令,防止阻塞IBR,同时删除BLOCK逻辑也有优化,优化FoldedTreeSet数据结构,FoldedTreeSet会随着时间推移,性能下降,重启可恢复正常。
- 短期方案(服务端)
在服务端,可以开启IBR参数,滚动重启dn实例和nn实例。
dfs.blockreport.incremental.intervalMse
调整为1000(单位:s)
该参数用于设置增量块报告的 MSE(Mean Squared Error)计算的时间间隔。在 Hadoop 中,NameNode 会定期生成块报告,以监控文件系统的健康状况。这些报告包含了有关文件系统的信息,如文件数量、存储空间使用情况等。其中,MSE 是一种常用的统计指标,用于衡量块报告数据的变化程度。较小的时间间隔意味着更频繁的报告,这有助于及时发现潜在的问题。然而,过小的时间间隔可能会导致过多的计算和网络开销。通常情况下,建议将此参数设置为较长的时间间隔,例如几个小时或一天。这样可以降低对系统性能的影响,同时确保及时获取到块报告的更新信息。如果需要更细粒度的控制,可以根据实际需求调整此参数的值。
该参数的默认值为0,当调整为大于零的数字时,表明已开启IBR,关于IBR的说明,可参见:blog.csdn.net/Androidlush…
dfs.namenode.file.close.num-committed-allowed
调整为1
该参数用于控制NameNode中可以同时关闭的文件数量。当文件被客户端关闭时,会向NameNode发送一个块(block)提交请求。这个参数限制了可以同时提交并关闭的文件数量。如果达到这个限制,那么其他文件的块提交请求将被阻塞,直到有文件块提交完成并关闭。这个参数有助于防止同时提交过多的文件块,导致NameNode压力过大。
值得说明的是,在服务端修改参数需要重启服务。
- 短期方案(客户端)
调整客户端参数,增加hdfs文件关闭失败的重试次数,防止作业因重试次数达到阈值而失败。
dfs.client.block.write.locateFollowingBlock.retries
调整为20。
在这个案例中,NameNode打印了多次checkFileProgress是由于HDFS客户端多次尝试close文件,但是由于当前状态不满足要求,导致close失败, HDFS客户端retry的次数是由参数dfs.client.block.write.locateFollowingBlock.retries决定的,如果此时再次调用close或者close的retry的次数增多,那么close都将返回成功。该参数的默认值为6,表示最大会重试6次,那么中间睡眠等待的时间为400ms、800ms、1600ms、3200ms、6400ms、12800ms,25600ms也就是说close函数最多要50.8秒才能返回。短期方案调大了重试次数和等待时长,提高了作业运行的稳定性,但并不能从根本上解决问题。