文件存储系统中,元数据操作占所有文件系统操作的30-70%,有效的元数据性能对于整体系统性能至关重要。由于元数据是独立于数据进行管理的,元数据服务器(MDS)的负载几乎全部由较小的读写操作(元数据都比较小)组成,因此MDS优化自己与底层存储设备的I/O至关重要。常规的write buffering和数据预读技术减少了与存储设备进行数据I/O交互,这仅仅是针对数据,元数据方面也可以借鉴这两种方法。
inode预取
减少与存储设备进行读I/O交互,那就得用缓存。数据预读技术就是利用了缓存的局部性原理中的空间局部性(被用过的存储器位置附近的数据很可能将被再次被引用)。元数据的读取也可以利用空间局部性原理,MDS去读取目录分片下的某个文件/目录的inode时,可以先将整个分片读上来,之后MDS去读该目录分片下的其他文件/目录的inode时,就直接去缓存中拿,这样性能就上来了。这叫inode预取。根据具体的流程来分析inode预取过程:如ls /test/test1,test1为文件。从MDCache::path_traverse开始看,整个函数调用流程如下
![17657ed8d51669818f9e58105dcb8831.png](https://i-blog.csdnimg.cn/blog_migrate/f659a95eadfb55ec628683c05cbfef7f.jpeg)
代码如下
int MDCache::path_traverse(MDRequestRef& mdr, Message *req, MDSInternalContextBase *fin, // who
const filepath& path, // what
vector<CDentry*> *pdnvec, // result
CInode **pin, int onfail)
{
// onfail = MDS_TRAVERSE_FORWARD
bool discover = (onfail == MDS_TRAVERSE_DISCOVER); // discover = false
bool null_okay = (onfail == MDS_TRAVERSE_DISCOVERXLOCK); // null_okay = false
bool forward = (onfail == MDS_TRAVERSE_FORWARD); // forward = true
...
CInode *cur = get_inode(path.get_ino()); // 获取"test的CInode
// start trace
if (pdnvec) pdnvec->clear();
if (pin) *pin = cur; // *pin此时就是"test"的CInode
unsigned depth = 0;
while (depth < path.depth()) {
// path.depth() = 1
// open dir
frag_t fg = cur->pick_dirfrag(path[depth]); // 找出对应的分片,此时应为frag_t()
CDir *curdir = cur->get_dirfrag(fg); // 从分片中找出对应的CDir,即"test"的CDir, 此时并没有"test"的CDir
if (!curdir) {
if (cur->is_auth()) {
curdir = cur->get_or_open_dirfrag(this, fg); // 新建"test"的CDir
} else {
... }
}
// dentry
CDentry *dn = curdir->lookup(path[depth], snapid); // 从"test"的CDir中找"test1"的CDentry,此时为NULL
CDentry::linkage_t *dnl = dn ? dn->get_projected_linkage() : 0; // dn为NULL, 所以dnl = 0
// MISS. dentry doesn't exist.
...
dout(12) << "traverse: miss on dentry " << path[depth] << " in " << *curdir << dendl;
if (curdir->is_auth()) {
// dentry is mine.
if (curdir->is_complete() ||
(snapid == CEPH_NOSNAP &&