所有 Hadoop 进程都在 Java 虚拟机 (JVM) 上运行,每个守护进程都在集群中主机自己的 JVM 上运行。一般来说,生产集群的HDFS会配置NameNode HA,即有两个NameNode角色,每个NameNode都使用自己的JVM。NameNode JVM的heap预估是个技术活,本文主要介绍相关知识,另外NameNode的heap使用主要来源HDFS中目录,文件和block数量,为了HDFS的稳定和最佳性能,一般建议HDFS中的文件数不要超过3亿。
1 NameNode 堆内存大小的环境变量
我们可以配置 HADOOP_HEAPSIZE
和 HADOOP_NAMENODE_OPTS
的值来调整 NameNode 堆内存的大小。HADOOP_HEAPSIZE
设置所有 Hadoop服务(如HDFS、YARN 和 MapReduce)的 JVM 堆大小。HADOOP_HEAPSIZE
将一个整型值的最大内存 (Xmx) 参数传递给 JVM,比如:
HADOOP_HEAPSIZE=1024
HADOOP_NAMENODE_OPTS
是NameNode的专有配置,需要设置必须指定的所有 JVM flag,HADOOP_NAMENODE_OPTS
会覆盖NameNode的HADOOP_HEAPSIZE
Xmx值,例如:
HADOOP_NAMENODE_OPTS=-Xms1024m -Xmx1024m -XX: UseParNewGC -XX: UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX: CMSParallelRemarkEnabled -XX: PrintTenuringDistribution -XX:OnOutOfMemoryError={{AGENT_COMMON_DIR}}/killparent.sh
HADOOP_NAMENODE_OPTS
和 HADOOP_HEAPSIZE
都存储在 /etc/hadoop/conf/hadoop-env.sh 中。
2 监控堆内存使用情况
可以使用多种方法来监控堆内存使用情况:Cloudera Manager、NameNode Web UI 或命令行。
- • Cloudera Manager:从NameNode图表中找到堆内存使用的图表,或者自己构建:
select jvm_max_memory_mb, jvm_heap_used_mb where roleType="NameNode"
- • NameNode Web UI:向下滚动到“Summary ”查找“Heap Memory used”。
- • 命令行:生成heap dump
3 文件和目录
HDFS元数据的持久化是通过fsimage文件和edits文件来实现的,不要尝试修改元数据目录或文件,修改可能会导致 HDFS 停机,甚至永久数据丢失。
1.fsimage
包含文件系统在某个时间点的完整状态,每次文件系统修改都会分配一个唯一的、单调递增的事务 ID。 fsimage 文件表示直到特定事务 ID 为止的所有修改后的文件系统状态。
2.edits file
包含一个日志,列出在最新的 fsimage 之后进行的每次文件系统更改(文件创建、删除或修改)。检查点是合并最新 fsimage 内容的过程,它会合并所有的edits,并创建一个新的fsimage,检查点可以由配置策略自动触发或由 HDFS 管理命令手动触发。
4 磁盘空间与命名空间
HDFS 默认的block size(dfs.blocksize) 为 128 MB,NameNode 上的每个命名空间(namespace)对象大约消耗 150 个字节。在 DataNode 上,数据文件占用的空间是文件的真实大小,而不是按照多少个block size来占用空间。比如192MB的文件占用192MB的磁盘空间,而不是块大小的整数倍。使用默认块大小 128 MB,192MB的文件会被分割为两个块文件,一个 128 MB 文件和一个 64 MB 文件。在NameNode中,命名空间对象是通过文件和块的数量来衡量的。还是这个192BM的文件,它由三个命名空间对象(1 个文件 inode 2 个block)组成,并消耗大约 450 bytes的内存。
大文件一般会被拆成较少数量的block,所以比小文件会消耗更好的内存。一个 128 MB 的数据文件由 NameNode 上的两个命名空间对象表示(1 个文件 inode 1 个block),大约消耗 300 bytes的内存。 相比之下,128 个大小为 1 MB 的文件由 256 个命名空间对象(128 个文件 inode 128 个块)表示,消耗大约 38,400 bytes内存。因此对于内存管理和data locality优化,最佳split size应该是block size的整数倍。
默认情况下Cloudera Manager 为每百万个block分配最大堆空间 1 GB,实际需要多少内存取决于你的工作负载,尤其是每个命名空间中生成的文件、目录和block的数量。Cloudera建议每百万个block使用1GB的NameNode堆空间,主要考虑到命名空间对象,必要的bookkeeping数据结构和RPC工作负载,实际上真实的heap使用可能会低于这个值。
5 副本
默认的block复制因子(dfs.replication)是3,副本影响磁盘空间,但不影响内存消耗。副本会更改每个block所需的存储量,但不会更改block的数量。如果 DataNode 上的一个block文件被复制三次,则block文件的数量将增加三倍,但表示它们的block的数量不会增加三倍。如果关闭副本,一个 192 MB 的文件会占用 192 MB 的磁盘空间和大约 450 bytes的内存。如果有 100 万个此类文件或 192 TB 数据,则需要 192 TB 磁盘空间,在不考虑 RPC 工作负载的情况下,还需要 450 MB 内存:(100 万个 inode 200 万个block)* 150 bytes。启用默认3副本后,则需要 576 TB 的磁盘空间:(192 TB * 3),但内存使用量保持不变,还是 450 MB。如果考虑bookkeeping 和RPC调用,按照之前的建议,更安全的估计是 2 GB 内存。
6 NameNode堆内存预估示例
6.1 示例1:预估NameNode堆内存的使用
Alice、Bob 和 Carl 再磁盘上各有 1 GB (1024 MB) 的数据,但被分割成不同大小的文件。Alice 和 Bob 的文件是block size的整数倍,会消耗较少的内存,而Carl则全是1MB的小文件,所以会消耗更多的内存。
1.Alice: 1 x 1024 MB file
- • 1 file inode
- • 8 blocks (1024 MB / 128 MB)
Total = 9 objects * 150 bytes = 1,350 bytes of heap memory
2.Bob: 8 x 128 MB files
- • 8 file inodes
- • 8 blocks
Total = 16 objects * 150 bytes = 2,400 bytes of heap memory
3.Carl: 1,024 x 1 MB files
- • 1,024 file inodes
- • 1,024 blocks
Total = 2,048 objects * 150 bytes = 307,200 bytes of heap memory
6.2 示例2:预估需要的的 NameNode 堆内存
在此示例中通过考虑集群的总存储容量来预估内存,两个集群物理存储 4800 TB,按默认block size为128MB计算,大约 3600 万个block文件。
1.Cluster A:200台主机,每台24TB存储,集群一共4800TB存储。
- • Blocksize=128 MB, Replication=1
- • 集群总存储容量(MB):200 * 24,000,000 MB = 4,800,000,000 MB (4800 TB)
- • 每个block所需的磁盘空间:副本为1,需要128MB磁盘存储空间
- • 集群能保存的block数量:4,800,000,000 MB / 128 MB = 36,000,000 个block
按照之前建议的每百万block分配 1 GB 内存,Cluster A 需要 36 GB 的最大堆空间。
2.Cluster B:200台主机,每台24TB存储,集群一共4800TB存储。
- • Blocksize=128 MB, Replication=3
- • 集群总存储容量(MB):200 * 24,000,000 MB = 4,800,000,000 MB (4800 TB)
- • 每个block所需的磁盘空间:副本为3,需要384MB磁盘存储空间
- • 集群能保存的block数量:4,800,000,000 MB / 384 MB = 12,000,000 个block
按照之前建议的每百万block分配 1 GB 内存,Cluster B需要 12 GB 的最大堆空间。