HDFS 细粒度锁优化,FusionInsight MRS有妙招
摘要:华为云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的读写吞吐量,从而能够支持更多数据,更多业务请求访问,从而更好的支撑政企客户高效用数,业务洞见更准,价值兑现更快。
HDFS 细粒度锁优化,FusionInsight MRS有妙招的更多相关文章
- Java 中常见的细粒度锁实现
上篇文章大致说了下 ReentrantLock 类的使用,对 ReentrantLock 类有了初步的认识之后让我们一起来看下基于 ReentrantLock 的几种细粒度锁实现. 这里我们还是接着用 ...
- FusionInsight MRS:你的大数据“管家”
摘要:4月24日-26日,HDC.Cloud2021在深圳大学城成功举办,华为云FusionInsight MRS云原生数据湖带来最懂行的大数据解决方案,为政企客户提供湖仓一体.云原生的大数据解决方案 ...
- 解密华为云FusionInsight MRS新特性:一架构三湖
摘要:华为云安全网关产品总监郭冕在"华为云TechWave云原生2.0专题日"上发表<华为云FusionInsight MRS,一个架构实现三种数据湖>的主题演讲,分享 ...
- Java细粒度锁实现的3种方式
最近在工作上碰见了一些高并发的场景需要加锁来保证业务逻辑的正确性,并且要求加锁后性能不能受到太大的影响.初步的想法是通过数据的时间戳,id等关键字来加锁,从而保证不同类型数据处理的并发性.而java自 ...
- mysql 锁优化
一.myisam存储引擎锁优化 1.合理理由读写优先级MyISAM 的表锁,写互相阻塞的表锁,默认系统是写优先,可改为读有先:low_priority_updates=1如果我们的系统是一个以读为主, ...
- JVM-并发-线程安全与锁优化
线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...
- 深入理解java虚拟机(7)---线程安全 & 锁优化
关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...
- Java的锁优化
高效并发是从JDK 1.5到JDK 1.6的一个重要改进,HotSpot虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning).锁消除(Lo ...
- JVM中锁优化简介
本文将简单介绍HotSpot虚拟机中用到的锁优化技术. 自旋锁 互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力.而在很多 ...
随机推荐
- nodejs 实现 磁力链接资源搜索 BT磁力链接爬虫
项目简介 前端站点 项目效果预览 http://findcl.com 使用 nodejs 实现磁力链接爬虫 磁力链接解析成 torrent种子信息,保存到数据库,利用 Elasticsearch 实现 ...
- Python窗口学习之浅尝按键触发事件
一.窗口上敲键盘触发事件(以Enter键为例) 二.点击窗口按钮触发事件(以鼠标左键双击为例) 代码: import tkinter as tk root = tk.Tk() root.geometr ...
- 【每日日报】第三十二天---DataOutputStream写文件
1 今天继续看书 DataOutputStream写文件 1 package File; 2 import java.io.IOException; 3 import java.io.FileOutp ...
- java中如何知道一个字符串中有多少个字,把每个字打印出来,举例
9.6 About string,"I am ateacher",这个字符串中有多少个字,且分别把每个字打印出来. public class Test { static i ...
- 将本地项目上传到gitLab操作
在设置好SSH之后,执行下面的操作即可完成: git init // 初始化git status //查看提交文件状态git remote add origin git地址 // 连接 ...
- FreeSql的各种工程demo上新啦
FreeSql的各种工程demo GitHub | Gitee console,winforms nf461,vb,wpf,webapi,workerSevice,signalIR xamarinFo ...
- Mysql_事务_存储过程_触发器
一.什么是事务? 事务(Transaction),一般是指要做的或所做的事情.在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit).事务通常由高级数据库操纵语言或编程语言( ...
- 使用 NIO 搭建一个聊天室
使用 NIO 搭建一个聊天室 前面刚讲了使用 Socket 搭建了一个 Http Server,在最后我们使用了 NIO 对 Server 进行了优化,然后有小伙伴问到怎么使用 Socket 搭建聊天 ...
- python---反转链表
class Node: def __init__(self, data): self.data = data self.next = None class Solution: "" ...
- Java 8 学习记录
Java 8 学习记录 官方文档 https://docs.oracle.com/javase/8/ https://docs.oracle.com/javase/8/docs/index.html ...