写点什么

HDFS 细粒度锁优化,FusionInsight MRS 有妙招

  • 2022 年 5 月 24 日
  • 本文字数:3831 字

    阅读完需:约 13 分钟

摘要:华为云 FusionInsight MRS 通过 FGL 对 HDFS NameNode 锁机制进行优化,有效提升了 NameNode 的读写吞吐量,从而能够支持更多数据,更多业务请求访问,从而更好的支撑政企客户高效用数,业务洞见更准,价值兑现更快。


本文分享自华为云社区《FusionInsight MRS HDFS 细粒度锁优化实践》,作者:pippo。

背景

HDFS 依赖 NameNode 作为其元数据服务。NameNode 将整个命名空间信息保存在内存中提供服务。读取请求(getBlockLocations、listStatus、getFileInfo)等从内存中获取信息。写请求(mkdir、create、addBlock)更新内存状态,并将日志事务写入到日志服务(QJM)。


HDFS NameNode 的性能决定了整个 Hadoop 集群的可扩展性。命名空间性能的改进对于进一步扩展 Hadoop 集群至关重要。


Apache HDFS 整体架构如下:

Apache HDFS 交互信息如下:

痛点

HDFS NameNode 的写操作的性能受全局命名空间系统锁的限制。每个写操作都会获取锁并保留锁,直到该操作执行完成。这样可以防止写入操作的并发执行,即使它们是完全独立的,例如命名空间中的对象不相交部分。

什么是 Fine Grained Locking(FGL)

FGL【细粒度锁】的主要目的是通过在独立命名空间分区上用多个并发锁替换全局锁,允许写入操作的并发。

当前状态

HDFS 设计思路为一次写,多次读。读操作使用共享锁,写操作使用独占锁。由于 HDFS NameNode 元数据被设计为单个内存空间中的命名空间树,因此树的任何级别的写操作都会阻塞其它写操作,直到当前写操作完成。虽然写是一次,但是当涉及大量并发读/写操作时,这就会影响整体性能。


在 HDFS NameNode 中,内存中的元数据有三种不同的数据结构:

  • INodeMap: inodeid -> INode

  • BlocksMap: blockid -> Blocks

  • DataNodeMap: datanodeId -> DataNodeInfo


INodeMap 结构中包含 inodeid 到 INode 的映射,在整个 Namespace 目录树种存在两种不同类型的 INode 数据结构:INodeDirectory 和 INodeFile。其中 INodeDirectory 标识的是目录树中的目录,INodeFile 标识的是目录树中的文件。


BlocksMap 结构中包含 blockid 到 BlockInfo 的映射。每一个 INodeFile 都会包含数量不同的 Block,具体数量由文件大小以及每个 Block 大小来决定,这些 Block 按照所在文件的先后顺序组成 BlockInfo 数组,BlockInfo 维护的是 Block 的元数据;通过 blockid 可以快速定位 Block。


DataNodeMap 结果包含 datanodeid 到 DataNodeInfo 的映射。当集群启动过程中,通过机架感知逐步建立起整个集群的机架拓扑结构,一般在 NameNode 的生命周期内不会发生大变化。


通过 INodeMap 和 BlocksMap 共同标识存储在 HDFS 中的每个文件及其块的信息。随着文件数量的增加,此数据结构大小也会随之增加,并对单个全局锁的性能产生很大影响。下面我们采用简单的文件目录树结构来演示现有的单一全局锁在文件系统的缺点。

HDFS NameNode 内存目录树结构


如上图所示,/D11/D21/D31/F2 和 /D12/D24/D38/F16 是不相交的文件,即有不同的父节点和祖父节点。可以看到 F2 和 F16 是两个独立的文件,对其中一个文件的任何操作都不应该影响另一个文件。

设计

如前所述,HDFS NameNode 将文件信息和元数据结构在内存中保存为一个目录树结构。当修改任意两个独立的文件时,第二次操作需要等到第一次操作完成并释放锁。释放锁以后,只有第二个操作获取锁后才能继续修改文件系统。类似的,后续操作也会阻塞,直到第二次操作释放锁。


在下面的例子中,我们考虑 2 个文件并发写入(创建、删除、追加。。。)操作。F2 和 F16 是文件系统下的 2 个独立文件(具有不同的父节点和祖父节点)。在将内容追加到 F2 时,F16 也可以同时进行修改。但是由于整个目录树全局对象锁,对 F16 的操作必须等对 F2 的操作完成后才能执行。


代替全局锁,可以将锁分布在一组名为“分区”的文件中,每个分区都可以有自己的锁。现在 F2 属于分区-1,F16 属于分区-2。F2 文件操作可以通过获取分区-1 的锁来进行修改,F16 文件操作可以通过获取分区-2 的锁来进行修改。


和以前一样,需要先获取全局锁,然后搜索每个文件属于哪个分区。找到分区后,获取分区锁并释放全局锁。因此全局锁并不会完全被删除。相反,通过减少全局锁时间跨度,一旦释放全局锁,则其它写操作可以获取全局锁并继续获取分区锁来进行文件操作。

分区的数量如何决定?如果有效的定义分区从而获得更高的吞吐量?


默认情况下,分区大小为 65K,溢出系数为 1.8。一旦分区达到溢出条件,将会创建新分区并加入到分区列表中。理想情况下,可以拥有等于 NameNode 可用 CPU 核数的分区数,过多的分区数量将会使得 CPU 过载,而过少的分区数量无法充分利用 CPU。

实现

引入新的数据结构-PartitionedGSet,它保存命名空间创建的所有分区信息。PartitionEntry 是一个分区的对象结构。LatchLock 是新引入的锁,用于控制两级锁--顶层锁和子锁。

PartitionedGSet

PartitionedGSet 是一个两级层次结构。第一层 RangeMap 定义了 INode 的范围,并将它们映射到相应的分区中。分区构成了层次结构的第二级,每个分区存储属于指定范围的 INode 信息。为了根据键值查找 INode,需要首先在 RangeMap 中找到对应键值的范围,然后在对应的 RangeSet,使用哈希值获取到对应的 INode。

HDFS NameNode 两级层次结构


RangeGSet 的容量有一定的阈值。当达到阈值后,将创建新的 RangeGSet。空的或者未充分利用的 RangeGSet 由后台 RangeMonitor 守护程序来进行垃圾回收。


HDFS NameNode 启动时,根据镜像中的 INode 数量计算合理的初始分区数。同时还需要考虑 CPU 核数,因为将分区数量提高到远超 CPU 核数并不会增加系统的并行性。


  • 动态分区:分区的大小有限,可以像平衡树一样可以进行分裂和合并。

  • 单个分区:只有一个分区,且只有一个与之相对应的锁,并且应和全局锁类似。这适用于小型集群或写入负载比较轻的集群。

  • 静态分区:有一个固定的 RangeMap,不添加或者合并现有分区。这适用于分区均匀增长的文件系统。而且这将消除锁定 RangeMap 的要求,允许并行使用锁。

Latch Lock

RangeMap 与 RangeGSet 分别有单独的锁。Latch Lock 是一种锁模式,其中首先获取 RangeMap 的锁,以查找与给定 INode 键对应的范围,然后获取与分区对应的 RangeGSet 的锁,同时释放 RangeMap 锁。这样针对任何其它范围的下一个操作都可以开始并发执行。


在 RangeMap 上持有锁类似于全局锁。目录删除、重命名、递归创建目录等几个操作可能需要锁定多个 RangeGSet。这要确保当前 HDFS 语义所要求的操作的原子性。例如,如果重命名将文件从一个目录移动到另一个目录,则必须锁定包含文件、源和目标目录的 RangeMap,以便使重命名成为原子。此锁定模式的一个理想优化是允许某些操作的 Latch Lock 与其他操作的全局锁结合使用。

INode Keys

HDFS 中的每个目录和文件都有一个唯一的 INode,即使文件被重命名或者移动到其它位置,该 INode 会保持不变。INode 键是以文件 INode 本身结尾,前面包含父 INode 的固定长度序列。


Key Definition: key(f) = <ppId, pId, selfId>


selfId 是文件的 INodeId,pId 是父目录的 INodeId,ppId 是父目录的父目录的 INodeId。INode 键的这种表达不仅保证了同级,同时也保证了表亲(相同祖父节点)在大多数情况下被分区到相同的范围中。这些键基于 INodeId 而非文件名,允许简单的文件和目录进行重命名,称为就地重命名,而无需重新进行分区。

效果

经过测试验证使用和不使用 FGL 功能性能,在主要写入操作情况下,吞吐量平均提高了 25%左右。

详细性能对比

使用 Hadoop NN Benchmarking 工具(NNThroughputBenchmark)来验证 NameNode 的性能。每个写入 API 验证并观察到平均 25%的性能提升。有很少一部分轻微或者没有提升的 API,分析并发现这些 API 均是轻量级 API,因此没有太大的提升。


NNThroughputBenchmark 是用于 NameNode 性能基准测试工具。该工具提供了非常基本的 API 调用,比如创建文件,创建目录、删除。在这个基础上进行了增强,从而能够支持所有写入 API,并能够捕获使用和不使用 FGL 的版本的性能数据。


用于测试的数据集:线程数 1000、文件数 1000000、每个目录文件数 40。

写入调用频率高的 API

其它内部写 API

常用读取 API:

通过完整的 FGL 实现,读取 API 也有很好的性能提升。

运行基准测试工具的命令:

./hadoop org.apache.hadoop.hdfs.server.namenode.NNThroughputBenchmark -fs file:/// -op create -threads 200 -files 1000000 -filesPerDir 40 –close

./hadoop org.apache.hadoop.hdfs.server.namenode.NNThroughputBenchmark -fs hdfs:x.x.x.x:dddd/hacluster -op create -threads 200 -files 1000000 -filesPerDir 40 -close

参考

与 FGL 相关的社区讨论

Hadoop Meetup Jan 2019 — HDFS Scalability and Consistent Reads from Standby Node, which covers Three-Stage Scalability Plan. Slides 21–25

社区中跟踪与 NameNode 可扩展性相关的其它 Jira

HDFS-5453. Support fine grain locking in FSNamesystem

HDFS-5477. Block manager as a service

HDFS-8286. Scaling out the namespace using KV store

HDFS-14703. Namenode Fine Grained Locking (design inspired us to implement it fully)

总结

华为云 FusionInsight MRS 云原生数据湖为政企客户提供湖仓一体、云原生的数据湖解决方案,构建一个架构可持续演进的离线、实时、逻辑三种数据湖,支撑政企客户全量数据的实时分析、离线分析、交互查询、实时检索、多模分析、数据仓库、数据接入和治理等大数据应用场景。

华为云 FusionInsight MRS 通过 FGL 对 HDFS NameNode 锁机制进行优化,有效提升了 NameNode 的读写吞吐量,从而能够支持更多数据,更多业务请求访问,从而更好的支撑政企客户高效用数,业务洞见更准,价值兑现更快。


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
HDFS 细粒度锁优化,FusionInsight MRS有妙招_hdfs_华为云开发者社区_InfoQ写作社区