HDFS NameNode内存全景

在HDFS系统架构中,NameNode管理着整个文件系统的元数据,维护整个集群的机架感知信息和DataNode和Block的信息,Lease管理以及集中式缓存引入的缓存管理等等。从整个HDFS系统架构上来看,NameNode是最重要、最复杂也是最容易出现问题的地方。

NameNode概述

NameNode管理的HDFS文件系统的元数据分为两个层次:NameSpace管理层,负责管理文件系统中的树状目录结构以及文件与数据块之间的映射关系;块管理层,负责管理文件系统中文件的物理块与实际存储位置的映射关系(BlockMap)。

NameSpace管理的元数据除内存常驻外,也会周期Flush到持久化设备fsimage文件上(core-site.xml中配置hadoop.tmp.dir目录下的dfs/name/current中);BlockMap元数据只存在于内存中;当NameNode发生重启,首先从持久化设备中读取fsimage,构建NameSpace的元数据信息,之后根据DataNode的汇报信息重新构造BlockMap,这两部分数据是占据NamNode大部分JVM Heap空间。

NameNode内存结构

namenode

NameNode常驻内存主要被NameSpace和BlockManager使用,二者使用占比分别接近50%。其它部分内存开销较小且相对固定,与NameSpace和BlockManager相比基本可以忽略。

NameNode内存分析

NameSpace

namespace

HDFS文件系统的目录结构也是按照树状结构维护,NameSpace保存了目录树以及每个目录/文件节点的属性。在整个NameSpace目录树中存在两种不同类型的INode数据结构:INodeDirectoryINodeFile。其中INodeDirectory表示的是目录树中的目录,INodeFile表示的是目录树中的文件。
inode

INodeDirectory和INodeFile均继承自INode,所以具备大部分相同的公共信息INodeWithAddititionalFields,除常用基础属性外,其中还提供了扩展属性features(如Quota,Snapshot等),如果以后出现新属性也可以通过feature扩展。不同的是,INodeFile特有的标识副本数和数据块大小组合的header(2.61之后新增了标识存储策略ID的信息)以及该文件包含的有序Block的数组;INodeDirectory特有的是列表children,children默认是大小为5的ArrayList类型,按照子节点name有序存储,在插入时会损失一部分写入的性能,但是可以方便后续快速二分查找提高读的性能,对于一般的存储系统,读操作比写操作占比要高。

BlockManager

NameNode概述中介绍的负责管理文件系统中文件的物理块与实际存储位置的映射关系(BlockMap)就是由BlockManager来统一管理。NameSpace和BlockMap之间通过前面提到的INodeFile有序Blocks数组关联到一起。
blockinfo

每一个INodeFile都会包含数量不等的Block,具体数量由文件大小及每一个Block的大小比值决定,这些Block按照所在的文件的先后顺序组成BlockInfo数组,BlockInfo维护的是Block的元数据,数据本身是由DataNode管理,所以BlockInfo需要包含实际数据且由DataNode管理的信息是名为triplets的Object数组,大小为3*replicas(replicas是Block副本数量,默认为3),从图中可以知道,BlockInfo包含了哪些Block,这些Block分别存储在哪些DataNode上。

如何快速的通过BlockId快速定位到Block,这里还需要BlocksMap。

BlocksMap底层通过LightWeightGSet实现,本质是一个链式解决冲突的Hash表。事实上,BlocksMap里所有的BlockInfo就是INodeFile中对应BlockInfo的引用,通过Block查找对应BlockInfo时,也是先对Block计算HashCode,根据结果快速定位到对应的BlockInfo信息。

blocksMap

这里还涉及到几个核心的数据结构:excessReplicateMap(多余的副本存放) ,neededReplications(需要补充Block的副本存放处,是一个优先级队列,缺少副本数越多的Block会优先处理),invalidateBlocks(删除的副本存放处) ,corruptReplicas(某些Block不可用暂存处)等。

相比Namespace,BlockManager管理的数据要复杂的多。

NetworkTopology

Hadoop在设计考虑到数据的安全和高效,默认存放三份副本。存储策略是本地一份,同机架内其他某一个节点上一份,不同机架的某一节点上一份,这样如果本地数据损坏了,节点可以从同一机架内的相邻节点拿到数据,速度肯定比从跨机架节点上拿到的数据要快;为了降低整体的带宽消耗和读取延时时间,HDFS会尽快让读取程序读取离它最近的副本。那么Hadoop确定任意两个节点是位于同一机架还是不同的机架呢?这就需要机架拓扑NetworkTopology,也叫作机架感知。
默认情况下,NameNode启动时日志信息是这样的:

1
2018-05-09 19:27:26,423 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:  /default-rack/ 192.168.123.102:50010

每个IP对应的机架ID都是/default-rack,说明Hadoop的机架感知没有被启用。
配置机架感知
配置机架感知也很简单,NameNode所在的节点中,在core-site.xml文件中配置topology.script.name,value通常是一个shell脚本,该脚本接受一个参数,输出一个值。接受的参数通常为某台DataNode的IP地址,而输出的值通常为该IP地址对应的DataNode所在的rack。

1
2
3
4
<property>  
<name>topology.script.file.name</name>
<value>/home/hadoop/apps/hadoop-2.6.5/etc/hadoop/topology.sh</value>
</property>

topology.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash  
HADOOP_CONF=/home/bigdata/apps/hadoop/etc/hadoop
while [ $# -gt 0 ] ; do
nodeArg=$1
exec<${HADOOP_CONF}/topology.data
result=""
while read line ; do
ar=( $line )
if [ "${ar[0]}" = "$nodeArg" ]||[ "${ar[1]}" = "$nodeArg" ]; then
result="${ar[2]}"
fi
done
shift
if [ -z "$result" ] ; then
echo -n "/default-rack"
else
echo -n "$result"
fi
done

topology.data格式为:节点(IP或主机名) /交换机xx/机架xx

1
2
192.168.123.102 hadoop02 /switch/rack2
...

配置后,NameNode启动时日志信息是这样的:

1
2018-05-09 19:27:26,423 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:  /switch/rack2/ 192.168.123.102:50010

说明Hadoop的机架感知已经被启用了。查看Hadoop机架信息的命令hadoop dfsadmin -printTopology

LeaseManager

Lease 机制是重要的分布式协议,广泛应用于各种实际的分布式系统中。HDFS支持Write-Once-Read-Many,对文件写操作的互斥同步靠Lease实现。
Lease实际上是时间约束锁,其主要特点是排他性。客户端写文件时需要先申请一个Lease,一旦有客户端持有了某个文件的Lease,其它客户端就不可能再申请到该文件的Lease,这就保证了同一时刻对一个文件的写操作只能发生在一个客户端。
NameNode的LeaseManager是Lease机制的核心,维护了文件与Lease、客户端与Lease的对应关系,这类信息会随写数据的变化实时发生对应改变。

本文是根据美团点评技术团队中《HDFS NameNode内存全景》整理总结