NameNode细粒度锁优化实践

HDFS NameNode中几乎所有的读写请求都依赖于一把全局锁做并发访问控制,成为整个系统最主要的性能瓶颈点。将全局锁做细粒度拆分之后,使得NameNode在不同读写比的场景下QPS得到数倍的性能提升

展开查看详情

1.NameNode 锁细粒度优化实践 美团点评 基础研发平台/数据平台中⼼/分布式存储组 万畅

2.⽬录 • HDFS 在美团现状 • 集群基本信息介绍 • NameNode 性能瓶颈分析 • 锁细粒度拆分 • FSNamesystem 与 BlockPool 锁拆分 • FSNamesystem 锁细粒度拆分

3.集群基本信息介绍 • 集群信息 • 基于 2.7.1, Federation + HA + Router 架构 • ⼀个集群,两个地域,四个机房 • 30+ NameSpace, 20,000+ DataNode • NameNode 信息 • FairCallQueue + backoff, 33 handler • 48 核 512G, JDK8 + CMS • 40亿+ 元数据,150亿+ ⽇均请求量

4.NameNode 性能瓶颈分析 当前 NameNode 存在很多性能问题 • 全局唯⼀的读写锁 • 写请求的同步 EditLog • ⾮读写分离架构

5.锁细粒度拆分规划 最终⽬标:能够同时处理不同⽂件/⽬录的读写请求 1. FS/BP 层拆分:FS BP 层耦合严重,需要先做拆分 2. FS 层细粒度拆分:客户端请求占⽐⾼,拆分收益⼤ 3. BP 层细粒度拆分

6.⽬录 • HDFS 在美团现状 • 集群基本信息介绍 • NameNode 性能瓶颈分析 • 锁细粒度拆分 • FSNamesystem 与 BlockPool 锁拆分 • FSNamesystem 锁细粒度拆分

7.FSNamesystem 与 BlockPool 锁拆分 ⽬标:访问两层的数据能够并⾏处理 Client: getFileInfo, getLising, getContentSummary, rename DataNode: IBR, BlockReport

8.死锁问题 两层之间耦和严重 FS 层⼤量依赖 BP 层数据的⽅法调⽤ BP 层对 FS 层数据有反向依赖 公共模块对两层都有依赖

9.解决死锁问题 死锁的必要条件 不可解:互斥条件、占有且等待、不可抢占 可解:循环等待 — 定义加锁顺序 ⽂件属性下沉,解除反向依赖 BP 层只需要 replication, storagePolicy Block 冗余存储 三种场景保证属性⼀致性 代码重构 数据结构、模块分层 FS/BP 提供统⼀的代码⼊⼝ 保证加锁顺序 fsLock —> bpLock 9

10.⼀致性问题 Active Node 内多线程并发读、写⼀致性

11.定义加锁模式 定义锁范围与包含关系 只访问 FS/BP 层数据,只获取 fs/bp lock 同时访问 FS, BP 层数据 FS 锁范围完全包含 BP 锁范围 重新加锁之后,需要检查状态

12.性能测试 不同读写⽐下,整体吞吐能⼒提升 30% ~ 100% 锁拆分前后 qps 16,000 12,000 8,000 4,000 0 1:1 4:1 10:1 12

13.⽬录 • HDFS 在美团现状 • 集群基本信息介绍 • NameNode 性能瓶颈分析 • 锁细粒度拆分 • FSNamesystem 与 BlockPool 锁拆分 • FSNamesystem 锁细粒度拆分

14.FS 层细粒度拆分 客户端请求中 所有请求都需要获取 FS 层锁,52% 的请求需要 BP 层锁 ⽬标:FS 层请求处理并⾏化 预期收益:吞吐提升⾄少 100% 客户端请求类型占⽐ 8% 7% 只写 FS 写 FS,BP 只读 FS 49% 读 FS,BP 37%

15.⽅案选型 INode 锁设计⽅案权衡 内存开销 死锁复杂度 并发度与灵活性 选 择 ⽅ 案

16.内存压⼒ 每个 inode 分配⼀个锁,内存开销巨⼤ 最多有 10 亿 inode, 每个锁对象⼤⼩ 100B 预计占⽤ 90G 堆内存 同⼀时刻内访问 inode 个数最多为 10000 线程数 * 路径个数 * 路径深度 按需分配,时间换空间 唯⼀性 访问同⼀个 inode 需要同⼀个锁对象 ⾼性能 最多需要⽀持 1000W qps 的并发请求

17.时间换空间 INodeLockManager map: 保存 id -> Lock 的映射关系 Queue: 缓存 Lock 对象 Monitor: 清理没有引⽤的 Lock 对象,后续复⽤ 避免 thread context switch ConcurrentHashMap + NonBlockingQueue

18.死锁问题 访问 InodeTree 的⽅式有多种,加锁顺序需要⼀致⽆冲突

19.INode 加锁⾏为 访问 inodeTree 时,区分读写⾏为 Query: 读数据。getFileInfo, getBlockLocations Modify: 修改数据属性。addBlock, setReplication Add_Del: 新增、删除。rename, delete /dir1/dir2/dir3/dir4/file Query root dir1 dir2 dir3 dir4 file 读锁 Modify root dir1 dir2 dir3 dir4 file 写锁 ⽆锁 ADD_DEL root dir1 dir2 dir3 dir4 file

20.INode 加锁顺序 正向遍历 inodeTree 从 root -> leaf 依次加锁 反向遍历 inodeTree tryLock 尝试去加锁 锁冲突则释放所有已加锁 重试直到成功或超过阈值 遍历多个路径 按照路径排序依次加锁

21.性能测试 测试环境 1亿⽂件+⽬录 Heap memory 512G Young memory 18G 60000 Handler 33 个 FS 层细粒度拆分 45000 Young memory 32G Handler 88 个 原⽣架构 30000 FS/BP层拆分 FS 层锁细粒度拆分 15000 0 1:1 4:1 10:1

22.其他说明 FS/BP 锁拆分 基于 HDFS-8966 进⾏开发 FS 层锁细粒度拆分 基于 HDFS-10662 进⾏开发 舍弃了 symbolLink, Snapshot 功能

23. 欢迎加⼊我们 美团点评 基础研发平台/数据平台中⼼/分布式存储组 致⼒于解决⼤数据场景多种存储需求 wanchang@meituan.com, 18559308701