前言

《HDFS NameNode内存全景》中,我们从NameNode内部数据结构的视角,对它的内存全景及几个关键数据结构进行了简单解读,并结合实际场景介绍了NameNode可能遇到的问题,还有业界进行横向扩展方面的多种可借鉴解决方案。

事实上,对NameNode实施横向扩展前,会面临常驻内存随数据规模持续增长的情况,为此需要经历不断调整NameNode内存的堆空间大小的过程,期间会遇到几个问题:

  • 当前内存空间预期能够支撑多长时间。
  • 何时调整堆空间以应对数据规模增长。
  • 增加多大堆空间。

另一方面NameNode堆空间又不能无止境增加,到达阈值后(与机型、JVM版本、GC策略等相关)同样会存在潜在问题:

  • 重启时间变长。
  • 潜在的FGC风险。

由此可见,对NameNode内存使用情况的细粒度掌控,可以为优化内存使用或调整内存大小提供更好的决策支持。

本文在前篇《HDFS NameNode内存全景》文章的基础上,针对前面的几个问题,进一步对NameNode核心数据结构的内存使用情况进行详细定量分析,并给出可供参考的内存预估模型。根据分析结果可有针对的优化集群存储资源使用模式,同时利用内存预估模型,可以提前对内存资源进行合理规划,为HDFS的发展提供数据参考依据。

内存分析

NetworkTopology

NameNode通过NetworkTopology维护整个集群的树状拓扑结构,当集群启动过程中,通过机架感知(通常都是外部脚本计算)逐渐建立起整个集群的机架拓扑结构,一般在NameNode的生命周期内不会发生大变化。拓扑结构的叶子节点DatanodeDescriptor是标识DataNode的关键结构,该类继承关系如图1所示。


图1 DatanodeDescriptor继承关系

在64位JVM中,DatanodeDescriptor内存使用情况如图2所示(除特殊说明外,后续对其它数据结构的内存使用情况分析均基于64位JVM)。


图2 DatanodeDescriptor内存使用详解

由于DataNode节点一般会挂载多块不同类型存储单元,如HDD、SSD等,图2中storageMap描述的正是存储介质DatanodeStorageInfo集合,其详细数据结构如图3所示。


图3 DatanodeStorageInfo内存使用详解

除此之外,DatanodeDescriptor还包括一部分动态内存对象,如replicateBlocks、recoverBlocks和invalidateBlocks等与数据块动态调整相关的数据结构,pendingCached、cached和pendingUncached等与集中式缓存相关的数据结构。由于这些数据均属动态的形式临时存在,随时会发生变化,所以这里没有做进一步详细统计(结果存在少许误差)。

根据前面的分析,假设集群中包括2000个DataNode节点,NameNode维护这部分信息需要占用的内存总量:

(64 + 114 + 56 + 109 ∗ 16)∗ 2000 = ~4MB

在树状机架拓扑结构中,除了叶子节点DatanodeDescriptor外,还包括内部节点InnerNode描述集群拓扑结构中机架信息。


图4 NetworkTopology拓扑结构内部节点内存使用详解

对于这部分描述机架信息等节点信息,假设集群包括80个机架和2000个DataNode节点,NameNode维护拓扑结构中内部节点信息需要占用的内存总量:

(44 + 48) ∗ 80 + 8 ∗ 2000 = ~25KB

从上面的分析可以看到,为维护集群的拓扑结构NetworkTopology,当集群规模为2000时,需要的内存空间不超过5MB,按照接近线性增长趋势,即使集群规模接近10000,这部分内存空间~25MB,相比整个NameNode JVM的内存开销微乎其微。

NameSpace

与传统单机文件系统相似,HDFS对文件系统的目录结构也是按照树状结构维护,NameSpace保存的正是整个目录树及目录树上每个目录/文件节点的属性,包括:名称(name),编号(id),所属用户(user),所属组(group),权限(permission),修改时间(mtime),访问时间(atime),子目录/文件(children)等信息。

下图5为Namespace中INode的类图结构,从类图可以看出,文件INodeFile和目录INodeDirectory的继承关系。其中目录在内存中由INodeDirectory对象来表示,并用List children成员列表来描述该目录下的子目录或文件;文件在内存中则由INodeFile来表示,并用BlockInfo[] blocks数组表示该文件由哪些Blocks组成。其它属性由继承关系的各个相应子类成员变量标识。


图5 文件和目录继承关系

目录和文件结构在继承关系中各属性的内存占用情况如图6所示。


图6 目录和文件内存使用详解

除图中提到的属性信息外,一些附加如ACL等非通用属性,没有在统计范围内。在默认场景下,INodeFile和INodeDirectory.withQuotaFeature是相对通用和广泛使用到的两个结构。

根据前面的分析,假设HDFS目录和文件数分别为1亿,Block总量在1亿情况下,整个Namespace在JVM中内存使用情况:

Total(Directory) = (24 + 96 + 44 + 48) ∗ 100M + 8 ∗ num(total children) Total(Files) = (24 + 96 + 48) ∗ 100M + 8 ∗ num(total blocks) Total = (24 + 96 + 44 + 48) ∗ 100M + 8 ∗ num(total children) + (24 + 96 + 48) ∗ 100M + 8 ∗ num(total blocks) = ~38GB

关于预估方法的几点说明: 1. 对目录树结构中所有的Directory均按照默认INodeDirectory.withQuotaFeature结构进行估算,如果集群开启ACL/Snapshotd等特性,需增加这部分内存开销。 2. 对目录树结构中所有的File按照INodeFile进行估算。 3. 从整个目录树的父子关系上看,num(total children)就是目录节点数和文件节点数之和。 4. 部分数据结构中包括了字符串,按照均值长度为8进行预估,实际情况可能会稍大。

Namespace在JVM堆内存空间中常驻,在NameNode的整个生命周期一直在内存存在,同时为保证数据的可靠性,NameNode会定期对其进行Checkpoint,将Namespace物化到外部存储设备。随着数据规模的增加,文件数/目录树也会随之增加,整个Namespace所占用的JVM内存空间也会基本保持线性同步增加。

BlocksMap

HDFS将文件按照一定的大小切成多个Block,为了保证数据可靠性,每个Block对应多个副本,存储在不同DataNode上。NameNode除需要维护Block本身的信息外,还需要维护从Block到DataNode列表的对应关系,用于描述每一个Block副本实际存储的物理位置,BlockManager中BlocksMap结构即用于Block到DataNode列表的映射关系。BlocksMap内部数据结构如图7所示。


图7 BlockInfo继承关系

BlocksMap经过多次优化形成当前结构,最初版本直接使用HashMap解决从Block到BlockInfo的映射。由于在内存使用、碰撞冲突解决和性能等方面存在问题,之后使用重新实现的LightWeightGSet代替HashMap,该数据结构本质上也是利用链表解决碰撞冲突的HashTable,但是在易用性、内存占用和性能等方面表现更好。关于引入LightWeightGSet细节可参考HDFS-1114。

与HashMap相比,为了尽可能避免碰撞冲突,BlocksMap在初始化时直接分配整个JVM堆空间的2%作为LightWeightGSet的索引空间,当然2%不是绝对值,如果2%内存空间可承载的索引项超出了Integer.MAX_VALUE/8(注:Object.hashCode()结果是int,对于64位JVM的对象引用占用8Bytes)会将其自动调整到阈值上限。限定JVM堆空间的2%基本上来自经验值,假定对于64位JVM环境,如果提供64GB内存大小,索引项可超过1亿,如果Hash函数适当,基本可以避免碰撞冲突。

BlocksMap的核心功能是通过BlockID快速定位到具体的BlockInfo,关于BlockInfo详细的数据结构如图8所示。BlockInfo继承自Block,除了Block对象中BlockID,numbytes和timestamp信息外,最重要的是该Block物理存储所在的对应DataNode列表信息triplets。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2016/5bf22702.png)
图8 BlocksMap内存使用详解

其中LightWeightGSet对应的内存空间全局唯一。尽管经过LightWeightGSet优化内存占用,但是BlocksMap仍然占用了大量JVM内存空间,假设集群中共1亿Block,NameNode可用内存空间固定大小128GB,则BlocksMap占用内存情况:

16 + 24 + 2% ∗ 128GB +( 40 + 128 )∗ 100M = ~20GB

BlocksMap数据在NameNode整个生命周期内常驻内存,随着数据规模的增加,对应Block数会随之增多,BlocksMap所占用的JVM堆内存空间也会基本保持线性同步增加。

小结

NameNode内存数据结构非常丰富,除了前面详细分析的核心数据结构外,其实还包括如LeaseManager/SnapShotManager/CacheManager等管理的数据,由于内存使用非常有限,或特性未稳定没有开启,或没有通用性,这里都不再展开。

根据前述对NameNode内存的预估,对比Hadoop集群历史实际数据:文件目录总量~140M,数据块总量~160M,NameNode JVM配置72GB,预估内存使用情况:

Namespace:(24 + 96 + 44 + 48) ∗ 70M + 8 ∗ 140M + (24 + 96 + 48) ∗ 70M + 8 ∗ 160M = ~27GB BlocksMap:16 + 24 + 2% ∗ 72GB +( 40 + 128 )∗ 160M = ~26GB

说明:这里按照目录文件数占比1:1进行了简化,基本与实际情况吻合,且简化对内存预估结果影响非常小。

二者组合结果~53GB,结果与监控数据显示常驻内存~52GB基本相同,符合实际情况。

从前面讨论可以看出,整个NameNode堆内存中,占空间最大的两个结构为Namespace和BlocksMap,当数据规模增加后,巨大的内存占用势必会给JVM内存管理带来挑战,甚至可能制约NameNode服务能力边界。

针对Namespace和BlocksMap的空间占用规模,有两个优化方向:

  • 合并小文件。使用Hive做数据生产时,为避免严重的数据倾斜、人为调小分区粒度等一些特殊原因,可能会在HDFS上写入大量小文件,会给NameNode带来潜在的影响。及时合并小文件,保持稳定的目录文件增长趋势,可有效避免NameNode内存抖动。
  • 适当调整BlockSize。如前述,更少的Block数也可降低内存使用,不过BlockSize调整会间接影响到计算任务,需要进行适当的权衡。

对比其他Java服务,NameNode场景相对特殊,需要对JVM部分默认参数进行适当调整。比如Young/Old空间比例,为避免CMS GC降级到FGC影响服务可用性,适当调整触发CMS GC开始的阈值等等。关于JVM相关参数调整策略的细节建议参考官方使用文档。

这里,笔者根据实践提供几点NameNode内存相关的经验供参考:

  • 根据元数据增长趋势,参考本文前述的内存空间占用预估方法,能够大体得到NameNode常驻内存大小,一般按照常驻内存占内存总量~60%调整JVM内存大小可基本满足需求。
  • 为避免GC出现降级的问题,可将CMSInitiatingOccupancyFraction调整到~70。
  • NameNode重启过程中,尤其是DataNode进行BlockReport过程中,会创建大量临时对象,为避免其晋升到Old区导致频繁GC甚至诱发FGC,可适当调大Young区(-XX:NewRatio)到10~15。

据了解,针对NameNode的使用场景,使用CMS内存回收策略,将HotSpot JVM内存空间调整到180GB,可提供稳定服务。继续上调有可能对JVM内存管理能力带来挑战,尤其是内存回收方面,一旦发生FGC对应用是致命的。这里提到180GB大小并不是绝对值,能否在此基础上继续调大且能够稳定服务不在本文的讨论范围。结合前述的预估方法,当可用JVM内存达180GB时,可管理元数据总量达~700M,基本能够满足中小规模以下集群需求。

总结

本文在《HDFS NameNode内存全景》基础上,对NameNode内存使用占比较高的几个核心数据结构进行了详细的介绍。在此基础上,提供了可供参考的NameNode内存数据空间占用预估模型:

Total = 198 ∗ num(Directory + Files) + 176 ∗ num(blocks) + 2% ∗ size(JVM Memory Size)

通过对NameNode内存使用情况的定量分析,可为HDFS优化和发展规划提供可借鉴的数据参考依据。

参考文献

[1] Apache Hadoop. https://hadoop.apache.org/. 2016. [2] Apache Issues. https://issues.apache.org/. 2016. [3] Apache Hadoop Source Code. https://github.com/apache/hadoop/tree/branch-2.4.1/. 2014. [4] HDFS NameNode内存全景. http://tech.meituan.com/namenode.html. 2016. [5] Java HotSpot VM Options. http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html.

HDFS NameNode内存详解相关推荐

  1. Hadoop:HDFS NameNode内存全景

    原文转自:https://tech.meituan.com/namenode.html 感谢原作者 一.概述 从整个HDFS系统架构上看,NameNode是其中最重要.最复杂也是最容易出现问题的地方, ...

  2. php 内存池,内存详解: 详解PHP内存池中的存储层_php

    php的内存管理器是分层(hierarchical)的.这个管理器共有三层:存储层(storage).堆(heap)层和 emalloc/efree 层.存储层通过 malloc().mmap() 等 ...

  3. NAND FLASH 内存详解与读写寻址方式

    目录: 第一章 绪论 1.1 课题来源 1.2 研究背景与意义     1.2.1 Flash介绍     1.2.2 NAND Flash介绍     1.2.3 NAND Flash与NOR Fl ...

  4. NAND_FLASH_内存详解与读写寻址方式

    一.内存详解 NAND闪存阵列分为一系列128kB的区块(block),这些区块是 NAND器件中最小的可擦除实体.擦除一个区块就是把所有的位(bit)设置为"1"(而所有字节(b ...

  5. realloc重分配内存详解_羽夜水之灵_百度空间

    realloc重分配内存详解_羽夜水之灵_百度空间 realloc重分配内存详解_羽夜水之灵_百度空间 realloc重分配内存详解 最近在写source code时需要在数组的buffer小时重新申 ...

  6. 内存详解-理解 JVM 如何使用 Windows 和 Linux 上的本机内存

    内存详解 理解 JVM 如何使用 Windows 和 Linux 上的本机内存 Java™ 堆耗尽并不是造成 java.lang.OutOfMemoryError 的惟一原因.如果本机内存 耗尽,则会 ...

  7. 内存详解mdash;mdash;理解 JVM 如何使用 AIX 上的本机内存

    理解 JVM 如何使用 AIX 上的本机内存 Java™ 堆耗尽并不是造成 java.lang.OutOfMemoryError 的惟一原因.如果本机内存 耗尽,则会发生普通调试技巧无法解决的 Out ...

  8. DDR3内存详解,存储器结构+时序+初始化过程

    转载 DDR3内存详解,存储器结构+时序+初始化过程 2017-06-17 16:10:33 a_chinese_man 阅读数 23423更多 分类专栏: 硬件开发基础 转自:http://www. ...

  9. JVM之对象内存详解

    JVM之内存详解 对象内存分配 JVM中对象创建流程 对象内存分配方式 指针碰撞 空闲列表 对比 内存分配安全问题 问题概述 解决方式 流程 ==对象内存分配以及GC详细流程-非常重要== ==进入老 ...

最新文章

  1. vue 动态生成路由菜单(从后端请求到菜单数据,生成左侧菜单栏)
  2. STC10F10XE定时器中断输出10KHz的方波程序
  3. 将RGB格式的颜色值转换为十六进制
  4. java swing实现计算器_Java swing实现的计算器功能完整实例
  5. Python装饰器、生成器、内置函数、Json-Day05
  6. jquery ajax json table 添加行,使用jQuery从AJAX响应构建表行(json)
  7. ai进入轮廓模式怎么退出_详解AI中扩展、扩展外观、轮廓化描边、创建轮廓
  8. 阿里文娱首次公开!AI 如何对爆款内容未卜先知?
  9. StyleGAN如何定制人脸生成
  10. oracle两种导出导入方式,即imp与impdp之比较
  11. ecshop根目录调用_ecshop调用指定商品分类下的商品
  12. [学习笔记]数据库设计概览
  13. new Option() 创建一个option标签
  14. python数字图像处理(14):高级滤波
  15. linq分类汇总怎么写_包围结构的字怎么写才好看?掌握方法是关键,分类总结要领很实用...
  16. java实体类属性比较_实体类之间属性对比的方法
  17. 「罗马人的故事I 罗马不是一天建成的」读后感
  18. 速腾(RoboSense)16线激光雷达调试出点云图(Ubuntu1804和windows系统都已经显示点云),包含各种遇见的坑【避坑指南】{[driver][socket]Rslidar poll}
  19. 北漂三年多 我选择离开,眼神更加坚定!
  20. ESP32 学习日志(5)——NVS

热门文章

  1. HDOJ 3415 Max Sum of Max-K-sub-sequence
  2. openMP学习笔记(一)
  3. Android中的动画有哪几类?各自的特点和区别是什么?
  4. 有人知道 I3C 吗?
  5. pythongui界面小程序_python界面小程序
  6. 事务嵌套问题_注意Spring事务这一点,避免出现大事务
  7. ftp 追加远程文件_远程办公彻底火了,高效办公,拒绝卡顿,远程利器你选对了么?...
  8. python成员运算符包括_Python 使用成员运算符案例
  9. 智慧交通day03-车道线检测实现03:相机校正和图像校正的实现
  10. 使用Pytorch处理多维特征的输入