分布式文件系统元数据设计概述
文章目录
- 引言
- BeeGFS元数据设计概述
- 简介
- 如何优化路径解析延迟
- 如何平衡元数据本地性和负载不均衡
- 如何处理缓存一致性
- InfiniFS元数据设计概述
- 简介
- 如何优化路径解析延迟
- 如何平衡元数据本地性和负载不均衡
- 如何处理缓存一致性
- Baidu-CFS元数据设计概述
- 简介
- 如何优化路径解析延迟
- 如何平衡元数据本地性和负载不均衡
- 如何处理缓存一致性
- FalconFS
- 简介
- 如何优化路径解析延迟
- 如何平衡元数据本地性和负载不均衡
- 如何处理缓存一致性
- 后记
- 参考资料
引言
这些年从FAST,OSDI等读了不少关于文件系统的论文,元数据服务设计方面关注得比较多,作个总结。
自从20年前,GFS/HDFS将元数据管理和数据存储分离,后续的文件系统便延用了这一架构并进行了一系列的优化和扩展,
约10年前的BeeGFS/Lustre/CephFS通过将元数据服务扩展到多节点,解决元数据服务的扩展性问题;约5年前的TectonicFS/InfiniFS通过元数据分层设计本地性和优化负载不均衡问题,约2年前Baidu-CFS/FalconFS通过引入数据库以及存算分离进一步优化扩展性和不均衡问题。
在GFS/HDFS那个时代,文件系统还主要用于存储大文件,文件数量相对也不多,所以最开始元数据服务(MDS)都是单节点;
而现在,文件系统的负载,已由海量且仍在快速增长的小文件占据,而且通过均衡分布数据到各数据服务(OSS)并行处理及采用新型存储介质数据访问已经变得极快,元数据性能及扩展性变得极其的关键,提升元数据服务的承载能力要解决的问题可以归纳为如下几个方面:
- 如何优化路径解析延迟
- 如何平衡元数据本地性及负载不均衡
- 如何处理缓存一致性
关于上述问题的处理方式, 我选取了不同时期的四款文件系统来说明,我们一起来了解下
BeeGFS元数据设计概述
简介
BeeGFS是一款开源的分布式并行文件系统(BeeGFS采用私有的开源协议,商业使用前请仔细阅读相关条款),采用典型的文件系统架构,包含:元数据服务(MDS)、数据服务(OSS)、客户端(Client)和管理服务(MGR);其中,MDS和OSS支持水平扩展,均采用镜像对的方式保证数据的可靠性(最终一致性),BeeGFS采用静态分区的方式将目录存储在一组MDS上,文件和父目录存储在同一组MDS,MDS采用本地文件系统(推荐ext4)来管理元数据,分别用dentries和inodes目录来放置目录项和索引节点,系统初始化时MDS将dentries和inodes目录均采用相同的哈希算法格式化为2层目录,运行时根据文件和目录的哈希值将目录项和索引节点放置到dentries和inodes的第二级目录中, 详细的元数据组织方式可以阅读我之前的文章<<BeeGFS元数据设计思想剖析>>。
如何优化路径解析延迟
文件系统采用树形结构来组织目录和文件,要访问某个文件需要从根目录开始逐级解析路径分量。在分布式文件系统中,如果客户端每解析一个路径分量都需要通过网络访问元数据服务,那么文件访问延迟将随着目录深度的增加线性增长,同时给元数据服务带来很大的负载压力,而且越靠近根目录所在的元数据服务负载压力越大(近根热点)。
BeeGFS的内核客户端采用VFS dcache来缓存目录项,通过内核的d_invalidation来失效缓存,为平衡性能和一致性,通常缓存失效时间设置得很短。对于以读写混合为主的场景,高频率的d_invalidation缓存失效以及元数据更新冲突,让DCache变得近乎无效;对于以读为主的场景,效果很好,如:AI训练中数据集。
如何平衡元数据本地性和负载不均衡
这两个问题本质上是元数据管理和数据存储分离后,分布式元数据服务架构带来的副作用,根据一次文件系统元数据操作涉及到的元数据对象数量,我们可以将常见元数据操作归纳为如下三类:
- 只涉及它自身的元数据对象的操作,如:open,close,stat
- 涉及它自身元数据及其父目录元数据对象的操作, 如:create,delete,mkdir,readdir
- 涉及多个元数据对象的操作, 如:rename
其中第二和第三类操作涉及到多个元数据对象,如果涉及的各元数据对象存储在不同的元数据服务上,意味着完成该次元数据操作涉及到多次网络操作,不仅延迟高而且为实现POSIX语义的强一致性,需要分布式事务保证原子性和隔离性,实现复杂。元数据操作本地性,就是尽可能减少一次元数据操作中的网络操作,减少延迟。
BeeGFS采用静态分区方式将目录及其下的文件放置在同一组MDS上,因此open,close,stat,create, delete, readdir等操作都只需要一次网络交互即可完成,mkdir和rename如果发生在同一组MDS上,也只需一次网络交互。
分布式文件系统中由于元数据服务采用的设计架构及业务负载的多样性,会出现某些元数据服务负载很高,而另外一些空闲的现象,这就是负载不均衡。BeeGFS在选择MDS时采用round-robin方式或者根据MGR的负载统计选择负载最小的MDS组做为新创建目录的目标MDS组,这在一定程度上可以缓解负载不均衡的问题,但由于BeeGFS采用静态分区方式将目录及其下的文件放在同一组MDS上, 如果业务访问集中在该目录上,将出现明显的负载不均衡(目录热点)问题。
如何处理缓存一致性
这里的缓存一致性是指客户端和元数据服务间的一致性,包含目录和文件。在前面的章节《如何优化路径解析延迟》,有讲到BeeGFS采用VFS dcache和d_invalidation机制来实现目录缓存的一致性,元数据服务通过在扩展属性(xattr)上记录版本号
来支持客户端的缓存失效。
对于文件缓存,BeeGFS实现了Native和Buffered两种缓存机制,Native机制实质上就是VFS层的dcache和icache,文件数据缓存在pagecache中,每个文件的缓存数据由inode中address_space指向的radix tree或xarray管理,挂载BeeGFS客户端时会创建flush工作队列,用于脏页回写,内核在合适的时候(内存不足、用户发起fsync、定期flush等)调用内核flush线程将数据回写到后端设备。Buffered机制仍然使用VFS层的dcache来缓存目录项,但使用自定义的全局红黑树来管理icache,文件数据缓存在全局的bufferpool中,每个文件的缓存数据由inode中的CacheBuffer指向的buf管理,挂载BeeGFS客户端时会创建flush工作队列和flush线程,默认每5s遍历红黑树,回写脏页。关于Native和Buffered缓存的详细介绍请阅读我之前的文章《BeeGFS客户端的文件缓存》。
InfiniFS元数据设计概述
简介
下述InfiniFS的内容来自我对FAST 2022的论文<<InfiniFS: An Efficient Metadata Service for Large-Scale Distributed Filesystems>>
的学习解读,关于InfiniFS的更多内容可以阅读我之前的文章<<InfiniFS论文解读>>或者直接阅读论文
InfiniFS是阿里集团研发的一款支持横向扩展的分布式元数据服务,包括:客户端(Client),元数据服务(MDS)和重命名协调器(Rename Coordinator),InfiniFS将重命名操作分离到重命名协调器,主要用于处理目录重命名的环问题,其元数据服务的三条核心设计原则紧扣文章中的三大问题,分别是:
- 可预测的目录ID:通过三元组<parentID,name,version>哈希值推断目录ID,加快路径解析
- 解耦目录元数据:将目录元数据拆分为描述自身的access stat和描述其下文件的content stat
- 延迟失效客户端缓存:由元数据服务在处理客户端请求时失效客户端缓存
下面我们一起来学习下InfiniFS中上述三条设计原则是怎么做的。
如何优化路径解析延迟
文件系统采用树形结构来组织目录和文件,分布式文件系统中通常采用二元组<parentID, name>来表示路径分量(关于全路径哈希(full-path hash)和路径遍历索引(path-walking index)两种索引方式的优缺点,读者可以将问题丢给AI chatbot快速得到答案
),InfiniFS中将创建目录时所在的父目录称为目录的生父(birth parent
),然后使用三元组<parentID, name, version>来计算目录ID,其中parentID为生父的ID,version用来保证ID的全局唯一性,目录ID的生成规则如下:
- 创建目录,根据<parentID,name, version>生成目录ID,parentID是生父ID,name是目录名,version是目录名版本,version的设置规则是:初始为0,再在该父目录下创建同名目录时+1(发生在以name命名的目录重命名后);
- 重命名目录,只更新目录的access stat条目的key(父目录变了,<parentID’,name, version>),目录的context stat条目及id都不变,这样可以保证该目录下的子条目均保持不变;目录的context stat条目中有一个重命名列表(RL),用于记录出生在此已被移到其他地方的子目录信息,而目录的access stat条目中有一个反向指针记录其生父及自身的版本;例如:在某子目录首次重命名时,在其生父的content stat的重命名列表(RL)添加<name, version>记录,记录目录名及其版本,在子目录自身的access stat中添加反向指针(BP)<parentID, version>记录,记录生父ID及目录版本,这时再创建同名的子目录,目录ID三元组为<parentID, name, version + 1>, 因为检查生父的重命名列表(RL)发现之前有同名的目录移走了。注意:首次重命名这前提,当一个子目录再次被重命名,就只有它的access stat条目的key发生变化(因为父目录变了),context stat条目,BP以及生父的RL都不变。
- 删除目录,删除重命名的目录时借助access stat中的反向指针BP<parentID, version>找到生父,从生父的重命名列表RL删除<name,version>记录,这样再次创建同名的子目录,version就又变成0了。注意:删除有重命名目录的生父目录时,只删除生父目录的access stat及content stat条目,而content stat条目中的RL列表会保存下来,直到所有的重命名目录都被删除后,生父的RL才会变清空。很好理解:生父目录删除后,还可以再创建,再创建后可以重用之前的RL列表从而避免冲突。
- 哈希冲突,三元组<parentID, name, version>哈希也可能冲突,同样地,用RL和BP来处理。
目录ID的生成规则看起来有些复杂,核心就是把目录ID静态固定下来,白话解释下
就是:和身份证类似,目录首次创建(出生),目录ID就确定了,不管搬家(重命名)几次都不变;如果目录名字被占用了(哈希冲突或者重命名),就给编个号(version);为了防止走失或者忘记,生父用小本本(重命名列表RL)记录下每个离家的孩子(重命名的目录)的名字和排行号(version),自己(目录)也用小本本(反向指针)记下老父亲的名字(生父ID)和排行号(version)。
目录ID生成规则确定后,为接下来的路径解析奠定了基础:
- 客户端从根目录开始,先用<parentID, name, 0>计算每个中间目录的ID;
- 客户端对所有的中间目录发起并发查找请求,元数据服务检查目录权限以及进行ID比较,如果与实际的ID不匹配,就将实际的ID返回给客户端;
- 重复过程1,2,直到路径解析完成。
很显然,这个机制是借助特殊的ID生成规则和并发来加速路径解析的;如果目录树不常变化,这个并发推断效果会很不错,当然客户端的目录缓存也能实现这个效果;如果目录树变化频繁,预测ID失败率很高,效果就不显著了。
如何平衡元数据本地性和负载不均衡
文件系统使用目录项dentry和索引节点inode作为载体来组织目录和文件,InfiniFS将目录元数据拆分为access stat和content stat两部分:access stat用于组织目录树,key为<parentID, name>, value主要包含:目录名,目录ID以及权限;content stat包含孩子列表(子目录和文件)及目录时间戳,这种拆分依据来自与上面<<BeeGFS:如何平衡元数据本地性和负载不均衡>>中元数据归类相同的观察,通过元数据拆分和分组,将子目录的access stat元数据和文件元数据组织在一起,给第一和第二类元数据操作带来很好的本地性;关于负载均衡,基于上述的元数据分组设计,InfiniFS将目录树拆分为独立的目录单元,然后根据目录ID将目录分布到不同的元数据服务节点上,来实现元数据的负载均衡。
InfiniFS对元数据的拆分和分组,与上面BeeGFS的元数据处理方式实际上是类似的,不同的是:BeeGFS使用本地文件系统(如:ext4)来组织元数据;而InfiniFS使用Rocksdb以Key-Value的形式存储目录及文件元数据,这种元数据拆分和分组,及存储方式与TectonicFS如出一辙。
因此,InfiniFS的元数据本地性和负载均衡表现与BeeGFS有很强的相似性,得益于Rocksdb LSM-T结构的优势,InfiniFS的每个元数据节点具有更强的元数据承载能力。
如何处理缓存一致性
InfiniFS客户端只缓存目录的access stat条目,缓存命中可以避免近根热点及带来稳定的路径解析延迟。客户端按照目录树结构组织缓存,用LRU列表组织叶缓存项,确保缓存淘汰时近根目录始终留在缓存中。如果目录树没有变化,缓存也足够,也就不需要关系一致性问题了,然而修改目录权限以及重命名都会导致缓存失效,InfiniFS使用延迟更新机制来保证缓存的一致性:发生目录权限或者重命名变更时,重命名协调器在元数据服务间广播元数据更新信息,客户端访问元数据时带上版本信息,由元数据服务延迟失效缓存。以目录重命名为例:
- 客户端向重命名协调器发送重命名请求,重命名协调器给收到的请求赋予递增的版本号;
- 重命名协调器给源目录和目标目录加锁,并行地将重命名请求连同版本号广播给元数据服务器处理(2PC协议),元数据服务会维护一个按版本号排序的失效列表,记录重命名信息,用于判断客户端请求的有效性;
- 将目录的access stat元数据从源元数据服务器移到目标元数据服务器,如果是首次重命名,更新生父的RL以及自身的BP。
客户端在进行路径解析时,并不会主动的校验缓存的有效性,它将当前缓存的目录名和版本号发送给元数据服务器,元数据服务器通过比较客户端请求信息与失效列表中的重命名信息判定有效性(只需比较请求版本号和最新版本之间的记录即可),如果比较结果为信息一致就处理请求,否则取消请求并将新的信息返回给客户端。
Baidu-CFS元数据设计概述
简介
随着数据库扩展能力的增强以及存算分离架构的兴起,文件系统架构也在往这个方向发展,如:VAST Data、WekaIO、JuiceFS、Baidu-CFS、FalconFS等,今天我们要介绍的是Baidu-CFS。
下述CFS的内容来自我对EuroSys 2023的论文<<CFS: Scaling Metadata Service for Distributed File System via Pruned Scope of Critical Sections>>的学习解读,关于CFS的更多内容可以直接阅读上述论文
CFS是百度集团研发的一款分布式文件系统,它由客户端(Client),分布式数据库(TafDB),数据服务(FileStore)和重命名器(Renamer)组成,可以发现CFS没有独立的元数据服务(MDS),它的文件系统POSIX语义功能由客户端和数据库联合实现了,这是它架构的最大特点;它的设计原理有三条,最终要解决的也是本文中最开始列出的三个问题:
- 分层元数据设计:命名空间层-目录树存在分布式数据库层,提升元数据操作的本地性,文件属性根据哈希分区存储在Key-Value中,提供更好的负载均衡;
- 宽松一致性事务及分层元数据设计,延迟GC,提供最终一致性;
- 精细的数据库原语及客户端轻量的元数据处理,消除元数据服务,减少网络通信量及延迟;
如何优化路径解析延迟
CFS通过客户端缓存元数据来加速路径解析,而且根据对POSIX元数据操作语义的分析及分层元数据设计,CFS消除了传统意义上的元数据服务,由客户端和分布式数据库TafDB协同完成元数据处理;对于create,unlink和目录内的rename操作,CFS实现了可参数化的单分片数据库原语,单分片原语将元数据操作涉及的多个元数据对象更新一次提交,大大减少了客户端和TafDB之间的网络通信量。
CFS对元数据属性更新冲突进行深入的挖掘,将其归纳为两类:
- 数值型属性,如:links,chiildren,size等,其更新的结果要么是增加要么是减少了某个数值
- 其他属性,如:time,permission等,其更新结果由最后的更新结果确定(覆盖更新)
基于上述的洞察,为进一步减少原语冲突带来的锁开销和延迟,单分片原语还采取了如下的优化措施:
- 对于数值型属性,引入增量应用合并(
delta apply merge
)过程来处理数值型属性的数值增减,将正增量和负增量编码到单分片原语,使得数据库可以按任意顺序应用这些更新 - 对于其他覆盖型更新属性,采用最后写者获胜的策略,TafDB依赖单调递增的时间戳来决定最终的更新值。
如何平衡元数据本地性和负载不均衡
CFS采用分层元数据设计,命名空间层-目录树存储在分布式数据库(TafDB)上,以获得更好的本地性能和负载均衡。
- 元数据本地性:目录树存储在一张超大
inode_table
中(与将元数据存储在多张表相比,可以避免维护跨表一致性带来的复杂性,更加的简单高效),每个目录的元数据拆分成inode id
记录,key为<parentID, name>, value包含id,type等, 和attr
记录, key为<ID, /_ATTR>(/_ATTR是保留关键字), value包含:children,links等,inode_table
中还包含file inode id
记录,key为<parentID, name>,value包含id,type等;通过在ID上进行区间/范围分区(range partition),元数据表被分成众多分片存储在TafDB中,这种分区机制将目录属性(attr
记录)和它的孩子ID记录(file inode id
记录)放在同一个分片,以取得本地性; - 元数据负载均衡:CFS的文件属性和文件数据一起存储在数据服务节点上,每个数据节点上部署一个rockydb实例,文件属性以Key-Value键值对存储在rocksdb中,key为,value为文件属性;通过在ID上进行哈希,文件属性均匀的分布到各数据服务节点,以取得负载均衡;
CFS的元数据分层设计得比前辈们(如:BeeGFS,InfiniFS)更加的精细,目录树存储在单张数据表中,目录属性及其孩子记录分组存储在单个分片中,文件属性则以哈希方式均匀分布到数据服务节点,文件系统的扩展上限由TafDB的表扩展能力决定。
如何处理缓存一致性
CFS更精细的元数据分层设计,也带来了更复杂的一致性问题,如:TafDB目录树属性和FileStore文件属性的一致性, 而目录重命名的一致性,最差情况下需要更新4个attr
记录和2个inode id
记录。与InfiniFS类似,CFS用独立的命名器(Renamer)服务来处理跨目录重命名,按照重命名是否跨目录分为快速路径和正常路径:
- 快速路径:是指目录内的文件重命名,使用预定义的片内数据库原语,在TafDB和FilStore的参与下完成,不需要命名器的协调,TafDB和FileStore之间元数据的一致性由后台GC延迟保证;重命名后的文件复用原文件的ID,因此重命名后的文件也复用FileStore上的原文件属性记录;
- 正常路径:是指跨目录的重命名,因为涉及跨分片的操作,片内原语不再适用,由命名器采用经典的加锁和2PC协议来协调TafDB和FileStore间的一致性及并发事务执行顺序;
为了解决客户端崩溃和网络分区导致的跨TafDB和FileStore的元数据一致性,CFS实现了后台GC:GC线程周期性或者按需启动,扫描TafDB和FileStore的WAL日志,收集近期的元数据修改,然后进行配对分析,清理不匹配/孤儿记录;
CFS论文中没有提到如何处理客户端缓存的一致性,通常的处理方式有:延迟失效,如:InfiniFS,超时失效,如:BeeGFS。
FalconFS
FalconFS是我最近看到的一篇有关分布式文件系统元数据优化的论文,读者可以搜索阅读论文<<FalconFS:Distributed File System for Large-Scale Deep Learning Pipeline>>,下文是我对论文的阅读总结。
简介
FalconFS是华为联合上海交大,针对自动驾驶场景中大量子目录,海量小文件的目录结构,以及数据集文件随机访问和突发访问的IO特点,设计的一款分布式文件系统, 为优化路径查找延迟、突发IO带来的负载不均衡以及减小客户端缓存占用,FalconFS采取混合元数据索引以及延迟命名空间复制的设计:
- 元数据分层,包括命名空间层和文件属性层;
- 客户端无状态,不缓存目录树;
- 延迟复制命名空间,每个元数据服务器包含整棵目录树,负责路径解析;
与典型的分布式文件系统类似,FalconFS也包含客户端,元数据服务,数据服务以及协调器,其中元数据服务用自定义扩展的PostgreSQL DB来存储元数据,使用数据库表和事务,B-link索引,xlog(WAL)以及主-备复制来管理元数据;协调器用于命名空间复制以及负载均衡调整。
如何优化路径解析延迟
为了兼容文件系统天然的目录树结构,FalconFS利用VFS已有能力实现了快捷路径解析:
- 中间路径解析,利用Linux kernel 5.7中,新引入的VFS标志LOOKUP_PARENT来标识中间分量,VFS发起lookup操作时,如果是中间分量会设置该标志,FalconFS截获该标志,返回伪造的中间分量,避免跨网络的中间分量lookup,直到末端分量才将请求发送给元数据服务
- 避免伪造的中间分量对用户可见,FalconFS用预定义的uid和gid来标识伪造分量,VFS的d_invalidate根据uid和gid来判断是否为伪造分量,如果lookup操作命中缓存,当前是末端分量且命中项的uid和gid是保留值,就从元数据服务获取真实的目录信息
另外,访问不存在的路径时,因为延迟复制命名空间,会带来一次额外的跨网络lookup。
如何平衡元数据本地性和负载不均衡
FalconFS采用混合索引方式,以期将文件属性均匀分布到各元数据节点,首先基于对华为自动驾驶数据集和开源数据集结构特点(存在大量的子目录,远远多于元数据节点)的洞察以及大数理论(数据样本足够大时,随机分布到有限空间的位置,近似均匀),根据文件名哈希将文件属性分布到不同的元数据节点;然后,优化由于热点文件名以及哈希偏差带来的不均性,采用选择性重定向(selective redirection)技术,使用异常表(exception table)来记录文件及其重定向方法,周期性将异常表中的文件移动到其他目标元数据节点,两种重定向处理方式如下:
- 路径遍历重定向:对于热点文件名带来的不均,根据父目录ID和文件名重新计算文件哈希,将文件移到目标元数据节点
- 覆盖重定向:对于哈希偏置带来的不均,将文件从高负载元数据节点移动到低负载元数据节点
FalconFS的各组件都有一份异常表副本,由协调器负责异常表的更新及同步,一旦异常表有更新,协调器会立即将最新的异常表推送给所有的元数据节点,而客户端采用延迟更新的策略,在出现访问冲突后,按需更新。协调器维护负载均衡原理如下:
- 元数据服务周期性上报,本地的inode数量以及出现最频繁的O(nlogn)个文件的出现次数,给协调器
- 协调器根据用户设置的平衡因子(e),调整各元数据服务上的inode数量,目标是:将各元数据服务持有的inode维持在总inode数(n)的(1/n + e),并使得异常表最小
- 协调器根据上报的数据,将元数据重新复制到其他节点,来逼近均衡:根据上报的统计信息,选出负载最大和最小的节点,根据最小化最大inode节点数量原则,选择重定向方式,将最大负载节点中频繁出现的文件重定向,对于路径遍历重定向,根据父目录ID和文件名哈希,将文件移到目标元数据服务;对于覆盖重定向,将文件从负载最大节点移到负载最小节点;然后更新异常表。重复这个过程,直到满足均衡目标。
以上,FalconFS聚焦在处理负载不均衡问题。
如何处理缓存一致性
FalconFS客户端没有缓存目录项,异常表的一致性,访问时延迟更新;元数据服务间的命名空间一致性,延迟复制更新。
对于异常表的一致性,客户端采用访问时延迟更新策略,在访问元数据服务出现冲突时,获取最新的副本;元数据服务上异常表,由协调器在更新异常表后,即刻推送给各元数据服务。
为支持无状态客户端,实现在服务端的路径解析,各元数据服务及协调器都维护一份完整的命名空间元数据,为减少维护元数据一致性的成本,采用了延迟复制和失效策略相结合的机制,即:延迟到访问时才同步,同时对于重命名和修改目录权限等改变目录树的操作,采取失效各元数据服务上涉及的子目录树的策略,而不仅是2PC协议。与InfiniFS和CFS类似,FalconFS也用独立的部件协调器负责,目录删除,目录重命名以及修改权限操作:
- 删除目录,协调器先给目标路径的所有祖先加共享锁,给目标目录加独占锁,将请求发送给目标元数据服务(目标目录所在元数据服务) - 先给目录加共享锁,阻塞后续的lookup请求,然后向其他的元数据服务广播失效请求,如果所有的元数据服务都返回目录为空,那么目标元数据服务删除目录,否则失败,最后处理结果返回给协调器,释放锁
- 修改目录权限,协调器先给目标目录加独占锁,将请求发送给目标元数据服务 - 先给目录加共享锁,向其他的元数据服务广播请求,修改目录权限 ,最后处理结果返回给协调器,释放锁
- 重命名目录,采用2PC协议保证一致性,协调器先给源路径和目标路径的所有祖先加共享锁,给源目录和目标目录加独占锁,然后广播请求给所有的元数据服务,加共享锁,失效源目录,在目标元数据服务上根据源目录inode生成目标目录的inode
以上,可以看到FalconFS的目录树更新都是比较’重量级’的操作。为了缓解锁冲突及提升元数据性能,FalconFS采取了多项优化措施,总结如下:
- 目录树更新时,协调器给中间目录加共享锁,给最后目录加独占锁;元数据服务给所有的目录都加共享锁,提升操作并发度
- 合并请求(锁合并+wal合并),元数据服务初始化数据库线程池,根据请求类型初始化请求队列,批量提交相同类型的请求
后记
终于码完了,希望各位读者读完后,有所收获。
分布式文件系统的元数据管理和数据存储分离架构已经走过超过20年了,相关的优化也到了一个阶段;受益于网络技术的发展,存算分离架构也开始在分布式存储中落地流行,如:vast data,wekaIO,xsky,huawei,值得关注。
参考资料
CFS:Scaling Metadata Service for Distributed File System via Pruned Scope of Critical Sections
InfiniFS:An Efficient Metadata Service for Large-Scale Distributed Filesystems
Facebook’s Tectonic Filesytem:Efficiency from Exascale
Improving the performance of BeeGFS parallel file system
FalconFS:Distributed File System for Large-Scale Deep Learning Pipeline