BeeGFS元数据管理设计思想剖析
Posted lizhongwen1987
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BeeGFS元数据管理设计思想剖析相关的知识,希望对你有一定的参考价值。
引言
换工作以后,对块存储和对象存储的关注有所减少,近两年,专注在分布式文件系统的研发上,为满足公司文件存储的业务需求,研发初期阅读了IO 500上有关文件存储的很多资料及论文,也调研了很多开源文件系统(如:Lustre,BeeGFS,DAOS,GekkoFS),其中BeeGFS是IO 500榜单上排名比较靠前的(新)文件系统,它是一款非常优秀的分布式文件系统,设计思路简单巧妙,性能表现非常优秀,我对此印象深刻,现将一些学习及研究心得分享出来,以飨读者,还请斧正。(注:BeeGFS采用的是私有开源协议,不可直接用于商业用途,本文纯技术交流,不做任何商业使用推荐)
第一篇:BeeGFS的元数据管理
概述
BeeGFS是一款针对海量小文件储存优化过的分布式并行文件系统,支持百亿级文件的储存,支持数以万计客户端的并行访问,它由client,mgmt,mon,mds,oss等核心组件组成:
- client是一个内核态模块,提供兼容posix的文件接口
- mgmt是单节点管理服务,用于管理集群拓扑、节点状态等系统配置信息
- mon是单节点监控服务,提供对集群状态、资源的可视化
- mds是元数据服务,用于文件系统元数据管理,支持两副本镜像,支持横向扩展
- oss是数据服务,用于文件系统数据存储, 支持两副本镜像,支持横向扩展
下图是BeeGFS的一个典型架构【附图来自BeeGFS开源主页】,与大家熟悉的HDFS,GFS,Lustre文件系统的架构高度相似,应该很好理解。
元数据管理
进入本文的主题,文件系统元数据管理不外乎解决:本地化和负载均衡两大问题,本地IO性能是最好的,将数据分布到不同的元数据节点上却能取得更好的负载均衡,看起来是一对矛盾,通常:
- 采用Hash的方式将目录分布到不同的元数据节点上,这能取得很好的负载均衡效果,但是牺牲了本地性,也会带来严重的分布式锁一致性开销,增加IO延迟,降低吞吐
- 采用目录子树分区的方式,能避免跨节点IO,带来很好的本地化性能,但易受不均衡的负载影响,导致负载不均衡
BeeGFS的元数据管理是怎么做得呢,又有什么特点呢,我们一起来了解下:
每个BeeGFS元数据服务,只能管理一个target(磁盘),推荐使用Ext4进行格式化,在元数据服务首次启动时,进行目录结构初始化,如下:
- nodeID和nodeNumID : 是节点的字符串ID和数字ID
- dentries和buddymir/dentries :目录项(dentry)目录树 ,128*128的二级目录(目录名为对应的十六进制),用户目录根据目录名Hash值放到对应的子目录;buddymir表示开启Mirror镜像,BeeGFS支持目录级别的Mirror,开启了Mirror功能的用户目录会放在buddymir/dentires下
- inodes和buddymir/inodes : 索引节点(inode)目录树,128*128的二级目录(目录名为对应的十六进制),用户目录索引根据目录名Hash值放到对应的子目录;buddymir表示开启Mirror镜像,BeeGFS支持目录级别的Mirror,开启了Mirror功能的用户目录会放在buddymir/dentires下
- root : 文件系统根目录(dentry:dentries/38/51/root, inode:inodes/38/51/root),如果后面开启了buddymir,根目录的目录项和索引节点会移到buddymir/dentires和buddymir/inodes相应的子目录下
注:由于采用相同的Hash算法,用户目录的dentry和inode会放到dentries(buddymir/dentires)和inodes(buddymir/inodes)相同的子目录下
BeeGFS元数据服务启动时,会向管理服务mgmt注册,对于新搭建的BeeGFS集群,mgmt会选取第一个注册的元数据服务mds作为整个文件系统的根节点,然后通过心跳广播给所有的元数据服务节点,各元数据服务节点会将该信息记录在根目录root的inode里。
前面我们提到:BeeGFS通过Hash将目录放到dentires和inodes目录子树中,其采用的Hash算法如下:
Paul Hsiehs hash ;生成32位Hash值,其中高16位作为第一级目录名,低16位作为第二级目录名,例如:通过上述算法,根目录root的第一级目录为38,第二级目录为51,目录项路径为:dentires/38/51/root, 索引节点路径为:inodies/38/51/root。
更一般地,一个用户目录的目录项路径为:dentries/$Level1Dir/$Level2Dir/$DirID,用户文件ID目录路径为:dentries/$Level1Dir/$Level2Dir/$DirID/#fSiDs#, 索引节点路径为:inodes/$Level1Dir/$Level2Dir/$DirID;用户文件ID目录存放该用户目录下所有用户文件以文件ID命名的文件列表,用户文件的元信息存储在上述ID文件的扩展属性(user.fhgfs
)上, 而用户文件的目录项通过硬链接指向该ID文件;用户目录的元信息存储在上述索引节点文件的扩展属性(user.fhgfs
)上;而父目录会在目录项路径下记录该目录的以目录名命名的目录项文件,如:
左图是示例目录树,中间的图是BeeGFS某元数据节点中的目录项子树,右边的图是同一元数据节点中的索引节点树
- 根目录(/):目录项ID为:root,目录项路径为:dentries/38/51/root, 索引节点路径为:inodes/38/51/root
- 文件(File1):目录项ID为:1-626E6AF2-B, 文件名为:File1, 目录项路径为:dentries/38/51/root/File1, 索引节点路径为:dentries/38/51/root/#fSiDs#/1-626E6AF2-B, 目录项文件硬链接到索引节点文件
- 目录(Dir1):目录项ID为:0-626E6C1F-B, 目录项路径为:dentries/8/62/0-626E6C1F-B, 索引节点路径为:inodes/8/62/0-626E6C1F-B
- 文件(File2):目录项ID为:0-626E6FA3-B,文件名为:File2, 目录项路径为:dentries/8/62/0-626E6C1F-B/File2, 索引节点路径为:inodes/8/62/0-626E6C1F-B/#fSiDs#/0-626E6FA3-B
注:根据负载均衡算法,Dir1可能与根目录分布在不同的元数据节点上,我的示例中凑巧在一个元数据节点上
观察上图的目录子树,我们可以得到如下结论:BeeGFS元数据管理是静态的基于目录的分区方式,文件与父目录放在一起:
- 目录:如上图的Dir1,目录的目录项文件是一个目录占位符,其下放置该目录下的所有文件的目录项文件及索引节点文件;目录索引节点文件与目录项文件同名,并放置在与目录项文件相同的二级路径下;索引节点文件包含该目录的所有元信息,并存储在扩展属性中。父目录的目录项中存放有子目录的目录名文件,该目录名文件的扩展属性中存储有父目录ID及目录ID等信息
- 文件:如上图的File1和File2,文件的目录项文件硬链接到索引节点文件,它们均放置在父目录的目录项文件(是一个目录)下;索引节点文件夹包含该文件的所有元信息,并存储在扩展属性中
结合目录项及索引节点核心结构及上文目录树结构,我们可以进一步得到如下结论:目录的目录项绿色部分与父目录分组存放,橙色部分与索引节点分组存放在一起,体现了本地化的设计, 右边的图是一个分组示例
性能分析
为了方便元数据操作性能分析,根据一次元数据操作中涉及到的元数据对象数量的不同,我们可以将常见元数据操作归为如下三类:
- 只涉及它自身的元数据对象的操作,如:open,close,stat
如:打开文件/File1, 只需要跟mds0通信,不涉及跨节点事务 - 涉及它自身元数据及其父目录元数据对象的操作, 如:create,delete,mkdir,readdir
如:枚举根目录(/),只需要跟mds0通信,不涉及跨节点事务 - 涉及多个元数据对象的操作, 如:rename
如: 重命名mv /File1 /Dir1/File1 , 需要与mds0和mdsx进行多次通信并需要加锁
读者注意:为了方便分析,简化了上述示例的交互过程(如:打开文件/File1,客户端需先发起LookIntent请求获取文件索引节点,再发起Open请求打开文件)
路径解析
文件系统采用树形结构来组织目录及文件的结构,文件系统根据目录逐级解析路径分量,也即:串行查找。我们再来分析上述的元数据操作:
- 只涉及它自身的元数据对象的操作,如:open,close,stat
1)打开文件/File1, 只需要跟mds0通信,不涉及跨节点事务
2)打开文件/Dir1/File2, 先查找目录(Dir1)跟mds0通信,再打开文件(File2)跟mdsx通信,不涉及跨节点事务 - 涉及它自身元数据及其父目录元数据对象的操作, 如:create,delete,mkdir,readdir
1)枚举根目录(/),只需要跟mds0通信,不涉及跨节点事务
2)枚举目录(/Dir1),先枚举根目录(/)跟mds0通信,再枚举目录(Dir1)跟mdsx通信,不涉及跨节点事务
读者注意:为了方便分析,简化了上述示例的交互过程(如:打开文件/File1,客户端需先发起LookIntent请求获取文件索引节点,再发起Open请求打开文件)
我们可以得到如下的结论:BeeGFS元数据操作具备本地化能力,但随着目录深度加大,路径解析导致元数据操作的RTT(round-trip time)线性增加
超大目录
根据上文的分析,BeeGFS采用静态的目录分区方式进行负载均衡,将文件与目录分组进行本地化,元数据服务底层磁盘采用ext4进行格式化,自然地,我们会想到:如果这个目录很大会怎么样,如:有1亿文件。很显然,受限于ext4的能力(其他文件系统可能可差,如:xfs),BeeGFS并不能高效的管理这种超大目录。
我们可以得到如下的结论:BeeGFS在超大目录上表现不佳
近根热点
我们在上文提到,BeeGFS采用的是基于目录的静态分区方式,文件系统初始化后,根目录的元数据节点即确定了,同时文件系统根据目录逐级解析路径分量,也即:在路径查找中根目录所在的元数据节点频繁被访问,成为热点。
我可以得到如下的结论:BeeGFS存在近根热点,存在目录热点问题
结论
BeeGFS采用基于目录的静态分区方式进行负载均衡,目录与文件分组方式进行本地化,在目录结构设计良好的情况下,性能表现优异,但存在如下问题:
- 串行路径解析
- 目录热点
- 超大目录
以上是关于BeeGFS元数据管理设计思想剖析的主要内容,如果未能解决你的问题,请参考以下文章