分布式文件系统KFS源码阅读与分析(一):MetaServer元数据组织结构

KFS文件系统的MetaServer元数据管理采用的是B+树方式,下面将结合其源码,对KFS MetaServer中元数据的组织形式及有关实现细节进行分析。

1. 相关源码文件

KFS MetaServer元数据管理的代码所在目录为kfs-[version]/src/cc/meta,其中,相关的源码文件有:

(1)meta/base.h:KFS元数据metadata中各节点的基础类,包括的类:类Key、类MetaNode,它们分别代表B+树种存储的数据、所有B+树节点的公共基础信息。

(2)meta/meta.hmeta/meta.cc:封装了metadata的基本数据定义,包括:类Meta、类MetaDentry、类MetaFattr和类MetaChunkInfo,它们分别代表文件系统中的目录项、文件或目录的属性项、对于一个文件偏移(file offset)的Chunk信息。

(3)meta/kfstree.hmeta/kfstree.cc:封装了对B+树中内部节点Node的各种操作及Tree的基本操作(与文件系统无关,B+树底层的实现),如插入节点、删除节点等。

(4)meta/kfsops.cc:封装了使用B+树存储KFS文件系统,实现的相关基本操作,如创建文件、删除文件、创建目录、删除目录等(作为Tree的实现)。

(5)meta/request.hmeta/request.cc:封装了对ChunkServerKfsClient发出的meta data请求的处理,通过Tree metatree执行相应的操作,实现对KFS文件系统各种基本操作的调用。

2. 为什么选用B+树

KFS的文件系统采用的是B+树,那么为什么选用B+树而不是B-树呢?这里做一个简单的分析:

2.1 B-树

B-树的定义:

B-树是一种平衡多路查找树,一棵m阶的B-树,或者是一颗空树,或者是满足下列特征的m叉树:

(1)树中每个节点至多有m棵字数;

(2)若根节点不是叶子结点,则至少有2棵子树;

(3)除根之外的所有非终端结点,则至少有[m/2]棵子树;

(4)所有的非终端结点中包含下列信息数据:(n, p0, k1, p1, k2, p2, ..., kn, pn),

其中:ki为关键字,且ki<ki+1;pi为指向子树根结点的指针,且满足pi所指子树中所有结点的关键字均大于ki且小于ki+1,pn所指子树中所有结点的关键字均大于kn;

(5)所有叶子结点均在同一层。

B-树的检索:

从根结点开始,对结点内的有序关键字序列进行二分查找,如果命中,则直接结束查找过程;否则,进入查询关键字所属范围的儿子结点进行查找。重复以上过程,直到所对应的儿子指针为空,或已经是叶子结点。

B-树的特性:

(1)关键字集合分布在整颗树中;

(2)任何一个关键字出现且只出现在一个结点中;

(3)搜索有可能在非叶子结点结束;

(4)其搜索性能等价于在关键字全集内做一次二分查找;

(5)自动层次控制。

2.2 B+树

B+树的定义:

B+树也是一种平衡多路查找树,它是应文件系统所需而出的一种B-树的变型树。一颗B+树满足以下条件:

(1)每个非终端结点至多有m颗子树;

(2)除根结点外,其他每个非终端结点至少有[(m+1)/2]颗子树;

(3)根结点至少有2颗子树;

(4)有n棵子树的结点中含有n个关键字;

(5)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接;

(6)所有的非终端结点可以看成是索引部分,仅包含其各个子结点中的最大关键字及指向子结点的指针。

通常来说,B+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。

B+树的检索:

B+树的检索方式分为两种:

(1)一种是从指向关键字最小的叶子结点的头指针开始,进行顺序查找;

(2)一种是从指向根结点的头指针开始,进行随机查找:与B-树基本相同,等价于在关键字全集做一次二分查找,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中)。

B+树的特性:

(1)所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

(2)检索时只有在叶子结点命中,不可能在非叶子结点命中;

(3)非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

(4)更适合文件索引系统。

2.3 B+树与B-树的比较

通过对B-树和B+树的定义及特性的了解,对两者进行比较:

(1)占用空间大小方面:

  • B-树的非叶子节点中含有大量的关键字信息,占用的空间相对比较大;
  • B+树中只有叶子节点中才有关键字信息,非叶子节点并没有指向关键字具体信息的指针,占用的空间相对比较小。

(2)检索路径长短方面:

  • 由于B+树的所有关键字都分布在叶子节点上,其他非叶子节点都是索引部分,因此树阶数(即树高)要比B-大,检索时要经过的路径就多,运算时间相对长一些;
  • 由于B-树的关键字分布到各个节点上,相对于B+树中完全分布到叶子节点上来说,分散分布的阶数自然要小,因此B-树的树阶数要比B+小,查找要经过的路径相对比较少,运算时间相对短一些。

对于文件系统的设计来说,最关键的瓶颈在于磁盘IO操作,如果占用的磁盘空间少的话,IO操作耗时自然就少。而真正检索内存中的数据结构(如B+树、B-树)的过程中,运算时间相对于磁盘IO操作来说要小的多,即内存的检索时间不是主要的瓶颈之处。

因此,虽然对于同阶的B-树和B+树,B+树的树高和平均检索长度均大于B-树 ,但实际上,检索过程中,最耗时的操作是磁盘IO操作,而B-树占用的空间相对较大,IO操作时劣势明显。由于B+树的非叶子结点无记录信息,只有索引,同样大小磁盘空间就可以存放更多的索引信息,检索访盘次数反而少,速度也就比B-树快。

2.4 选择B+树

B+树比B-树更适合实际应用中操作系统的文件索引和数据库索引,原因在于:

(1)磁盘读写代价低:即使B-树的运算时间相对于B+树来说较短,但由于磁盘IO操作方面的劣势,导致其总体上效率不如B+树

(2)查询效率更稳定:B+树中任何关键字的查找都必须经历从根结点到叶子结点,因此所有关键字查询的路径长度相同,每一个数据的查询效率相当。

3. 元数据组织结构

在KFS文件系统MetaServer元数据的实现中,有图示几种类型的B+树节点:


 

(1)MetaNode: 所有叶节点和内部节点的公共基础类,其中记录了不同树节点的类型信息。

(2)Node:表示内部节点,其中记录了树种内部节点的各种操作。

(3)Meta: 表示叶节点,而具体来说,不同的叶节点有:

  • MetaDentry: 文件目录项(Directory entry),实现从文件名到文件id的映射。
  • MetaFattr: 文件或目录属性,相当于KFS中的一个inode节点。
  • MetaChunkInfo: 对于一个文件偏移(file offset)的Chunk信息。

3.1 MetaNode

成员变量:

 

MetaType type;   //节点类型值
int flagbits; //标志位

 

构造函数:

MetaNode(MetaType t)        //初始化type=t, flagbits=0
MetaNode(MetaType t, int f) //初始化type=t, flagbits=f

3.2 Node

成员变量:

 

int count;                       //孩子节点个数
Key childKey[NKEY]; //孩子的key
MetaNode *childNode[NKEY]; //孩子节点
Node *next; //下一个相邻节点

 

构造函数:

Node(int f)    //初始化MetaNode中的节点类型type=KFS_INTERNAL,flagbits=f

3.3 Meta

成员变量:

fid_t fid;        //文件fid

构造函数:

Meta(MetaType t, fid_t id)  //初始化MetaNode中的节点类型信息type=t,及自身的fid=id

3.4 MetaDentry

成员变量:

 

fid_t dir;      //父目录的fid
string name; //目录项的名称
fid_t fid; //目录项的文件id

 

构造函数:

 

MetaDentry(fid_t parent, string fname, fid_t myID)

 

举例说明:通过Dentry结构实现/root/1.txt的查找过程:

(1) 获取”/”fid=2

dir=2, name=“/”, fid=2

(2) 获取”root”fid=8

dir=2, name=“root”, fid=8

dir=2, name=“usr”, fid=6

(3) 获取”1.txt”fid=12

dir=8, name=”1.txt”, fid=12

dir=8, name=”2.txt”, fid=13

dir=8, name=”3.txt”, fid=14

由以上查找过程可知,/root/1.txtfid12

3.5 MetaFattr

成员变量:

 

FileType type;          //类型(文件或目录)
int16_t numReplicas; //一个文件要求的备份数
struct timeval mtime; //修改时间
struct timeval ctime; //属性变更时间
struct timeval crtime; //创建时间
longlong chunkcount; //chunk数目
off_t filesize; //文件大小

 

构造函数:

MetaFattr(FileType t, fid_t id, int16_t n)
MetaFattr(FileType t, fid_t id,
struct timeval mt, struct timeval ct, struct timeval crt, longlong c, int16_t n)

3.6 MetaChunkInfo

成员变量:

 

chunkOff_t offset;  //chunk在文件中的偏移
chunkId_t chunkId; //chunk的标识符id
seq_t chunkVersion; //chunk的版本号

 

 

 

构造函数:

 

MetaChunkInfo(fid_t file, chunkOff_t off, chunkId_t id, seq_t v)
MetaChunkInfo(fid_t file, chunkOff_t off, chunkId_t id, seq_t v, CLVector
&m)

你可能感兴趣的:(分布式文件系统)