前言
本次cephfs优化,是通过增加cephfs中文件的元数据驻留内核的有效时间,从而减少访问元数据时内核态与用户态的切换、缩短元数据访问的io路径,达到提高元数据访问性能的目的。
相关组件介绍
cephfs(Ceph File System):在ceph 分布式对象存储上实现的文件系统。
mds(Metadata Servers):cephfs的元数据管理服务。
fuse(Filesystem in userspace):linux的用户态文件系统。Fuse包含一个内核模块和一个用户空间守护进程。内核模块加载时被注册成 Linux 虚拟文件系统的一个 fuse 文件系统驱动。此外,还注册了一个/dev/fuse的块设备。该块设备作为守护进程与内核通信的桥梁,守护进程通过/dev/fuse读取fuse request,处理后将reply写入/dev/fuse。
ceph-fuse:cephfs的客户端服务,同时是fuse的一个具体实现,即是fuse的一个守护进程。
ceph-fuse服务性能分析
在使用ceph的块存储和文件存储中,对比由rbd map映射得到的目录和由ceph-fuse挂载得到的目录性能,发现许多涉及大量元数据访问的操作(如 打开目录,ll目录等),前者性能比后者性能好。
可能原因1:rbd上的文件系统(ext4,xfs,下文统一使用rbd-ext4表示)的元数据存储在rbd本身的超级块中,访问的时候不需要像ceph-fuse一样与mds交互,因此rbd在元数据方面的性能优于cephfs。但是通过分析ceph-fuse日志发现,即使ceph-fuse缓存了大部分的元数据,这个情况下ceph-fuse极少会与mds交互。但是,rbd-ext4性能也比cephfs好。
可能原因2:rbd-ext4是本地内核态文件系统,ceph-fuse是用户态分布式文件系统,内核态文件系统比用户态文件系统性能好。证明内核态文件系统性能比用户态文件系统性能好,测试步骤如下:
分别使用用户态、内核态挂载cephfs,rbd到同一节,测试ls -l 一个包含3000文件的目录,统计时间。
内核态挂载 |
用户态挂载 |
|
cephfs |
0.09s |
2.30s |
横向对比cephfs两种挂载方式,可知用户态导致了cephfs元数据访问性能的下降。
用户态文件系统性能分析
一个文件对应一个entry(entry中记录目录/文件的名称), 一个 inode数据结构; ls -l 的过程就是要找出file1的entry,inode。对于一个 ls -l /root/test1/file1的操作,假如内核中没有缓存和有缓存的查找过程的区别如下:
有缓存:
1、在inode(root)节点下查找 name="test1"的entry。
2、命中缓存得到entry(test1),entry中有指向inode(test1)的指针,因此得到inode(test1)。
3、在inode(test1)节点下查找name="file1"的entry。
4、命中缓存并得到entry(file1),inode(file1)。
5、完成。
没用缓存:
1、在inode(root)节点下查找 name="test1"的entry。
2、没用命中缓存,通过FUSE模块向ceph-fuse进程请求查询"test1"的entry。
3、ceph-fuse将结果通过FUSE模块反馈到VFS模块。
4、在inode(test1)节点下查找name="file1"的entry。
5、没用命中缓存,通过FUSE模块向ceph-fuse进程请求查询"file1"的inode(file1)。
对比上述两个过程可知,没用缓存的情况下,查询文件元数据的操作,需要经过多次用户态/内核态的转换。内核态文件系统可以利用下图中vfs部分的cache来缓存元数据,缩短I/O路径用户态文件系统I/O路径更长,需要在用户态和内核态之间频繁切换。
优化方案
基于以上的分析知道,要提升fuse类型的文件系统元数据访问性能,在内核中缓存一部分元数据也许是一种不错的方法。一个用户态的文件系统,如上面所述,分为两部分:一部分运行在内核态;另一部分是由用户程序实现的,为了方便用户实现自己的文件系统,linux社区提供了libfuse 库,用户只需要实现libfuse中定义的接口就可以实现一个自己的文件系统。cephfs的客户端 ceph-fuse服务实际上就是实现了libfuse中的一些接口。
因此如果不想修改内核代码,只能修改libfuse,或者修改ceph-fuse代码。通过查询资料发现libfuse有提供相关的参数配置,即控制元数据entry,attr的有效时间,默认为1s.如下:
entry_timeout=T cache timeout for names (1.0s)\n"
attr_timeout=T cache timeout for attributes (1.0s)\n"
查看libfuse代码中的示例,知道这两个参数是在lookup方法返回entry的时候直接设置的,如下:
查看ceph-fuse的代码,发现ceph-fuse没有提供相应的配置。并且把entry_timeout, attr_timeout都设置为0,具体如:
因此通过修改ceph-fuse的代码,可以利用libfuse提供的功能特性,增加元数据entry,attr的有效时间,缩短I/O路径,也许能提升文件系统的性能。
方案验证
通过修改ceph-fuse的代码,把entry_timeout,attr_timeout设置为60后,测试ls -l 一个包含3000文件的目录耗时的耗时。
测试结果如下
|
耗时 |
op(lookup) |
op(getattr) |
op(getxattr) |
总op数量 |
ceph-fuse(0s) |
2.340s |
18014 |
6006 |
3003 |
27024 |
ceph-fuse(60s) |
0.461s |
0 |
4 |
3003 |
3008 |
测试数据分析:
- 把entry_timeout、attr_timeout设置为60s,可以提升元数据访问性能。大概提升5倍。
- ceph-fuse(60s)相比ceph-fuse(0s),ceph-fuse 只需要承受1/9的压力。主要是减少了lookup, getattr的操作。
结论:设置entry_timeout=60,entry_timeou=60能提升ceph-fuse元数据访问性能。也能减少ceph-fuse的压力。