
FUSE 为用户态空间下的文件系统框架方案,使用户可以在用户态下而非深入内核层就可以实现文件系统。FUSE 主要由三个部分组成:FUSE 内核模块、FUSE 库以及 mount 工具。FUSE 内核模块为系统内核为注册 FUSE 文件系统而实现的 kernel 接入层,类似于一个驱动层模块。FUSE 库为用户态下实现文件系统的相关库文件,mount 工具为挂载用户文件系统工具。
FUSE 文件系统主要由内核模块和 lib 库组成,内核模块在 Linux 下注册一个/dev/fuse的驱动设备,应用程序对于用户空间文件系统的操作请求由VFS转发写入/dev/fuse设备中,用户空间下的文件系统后台进程(daemon进程)会循环读取/dev/fuse设备中的请求并进行响应,然后将响应结果写回到/dev/fuse设备中并由VFS回复给请求应用进程,从而请求应用程序对该用户文件系统下的所有操作都是由文件系统进程来进行控制和实现,下面是FUSE的整体框架示意图。

1. SequoiaFS架构
SequoiaFS 将 SequoiaDB 中的某个集合映射到文件系统某个挂载点目录下,从而实现利用 SequoiaDB 作为挂载点目录的文件存储数据库,而且又支持对该挂载点目录的一系列文件系统API操作,从而实现利用文件系统API对 SequoiaDB 集合数据的操作。

SequoiaFS 是 FUSE 用户空间下的文件系统,在用户空间下通过 SequoiaDB 驱动层调用 SequoiaDB 数据库进行数据文件存储管理操作,同时作为 FUSE 文件系统,调用 FUSE 相关 lib 接口完成文件系统调用相关接口,实现 Application 用户软件在调用标准文件系统接口操作时对 SequoiaDB 的一系列操作,从而实现了分布式 NFS 文件系统。

2. 集合-文件映射关系
SequoiaFS 将指定的 SequoiaDB 目标集合映射挂载到某个目标路径mountpoint。在该路径下的所有文件和目录的操作都实际上是对 SequoiaDB 数据库的操作,映射示意图如下。

SequoiaDB 中的Lob对象集合映射到 SequoiaFS 文件系统的mountpoint映射点,而目录元数据集合mdcl记录目录文件信息,文件元数据集合mfcl记录文件信息,而Lob对象集合是文件的lob形式存储集合载体。从而在 SequoiaFS 文件系统下,所有的文件和目录都只是虚拟的文件和目录,即文件和目录都只实际存储在后台 SequoiaDB 中,而SequoiaFS 下所有的文件系统操作和调用都会实际转换到后台 SequoiaDB 对lob文件或者元数据集合的操作。实现利用文件系统API操作访问 SequoiaDB 数据文件。
从原理上,目录元数据和文件元数据是可以做成一个元数据集合的,但由于目录和文件在数目数量级上和访问上会有不同的差距,因此分开成两个元数据集合会有助于后续的扩展和元数据热点分离。并且目录元数据和文件元数据在记录结构上稍有差别。
3. 目录-文件归属
名称 | ||
Name | ||
对于目录遍历,有相对路径模式和绝对路径模式,但是在记录目录元数据的时候,目录则以记录相对路径(即目录父目录Pid和本身id,文件记录父目录pid)的方式进行。
根目录的id为1,根目录下的文件或目录则以1为Pid。元数据集合中文件记录以Pid和name作为唯一索引项,其子文件和子目录也类似处理。
对于遍历查找文件,如果要查找/a/b/c/下的d文件,则需要从根目录往下遍历,直找到d文件的pid和name,根据pid和name则可以找到d文件的元数据信息,如下图所示。

方案不采用记录绝对路径方式的原因是,此种记录方式存在大量冗余信息,在mv目录涉及rename操作时,要对子文件或目录做大量改写操作;同时一致性保证需要做得比较复杂,并且中间层目录访问权限无法控制。
基于上述原因选择记录相对路径的方式,但是记录相对路径的效率比较差,因此考虑对目录元数据做缓存机制,将目录缓存下来,以保证文件访问时的效率。
4. 目录缓存
在 SequoiaFS 下,访问目录和文件是频繁的热点操作,无论是ls查看还是文件读写,都需要大量的访问目录以验证文件是否存在及是否有权限操作等。所以为了减少与SequoiaDB的交互次数,将目录元数据文件进行缓存,将会大大提升SequoiaFS的性能。
由于目录和文件操作具有相关的耦合性,即当前操作的目录和文件在未来一段时间内也将大概率的进行再次操作,所以利用一个线程安全的LRU缓存作为目录的缓存区,将有效提高目录文件的操作性能。
在不同的节点机器上,可以同时mount同一个目标集合,这就意味着,在不同节点上很有可能对目标集合进行进行大量的不同操作,这就使得各个节点上的缓存会失效,这也是只对目录做缓存不对文件做缓存的原因之一,因为对目录的更改一般比文件更改少。一旦缓存失效,需要重新与SequoiaDB进行交互获取最新信息。
5. 文件句柄和并发操作
在FUSE内核模块层,有多个请求队列,background,pending,processing,interrupts和forgets,SequoiaFS 可以通过选项-o async_read或-o sync_read来指定SequoiaFS在读操作时进行异步IO模式还是同步IO模式。在异步IO模式下,用户的IO请求首先会发送到background队列中(用户可以根据选项-o max_background=N来设置background队列的大小)。同时,当background队列request请求达到最大值,容易导致IO被阻塞。异步读模式下,background队列中请求不断被发送 pending队列进行处理,待SequoiaFS完成read并返回数据时,request被触发读事件,表示本次io完成。由此可见,回到SequoiaFS层,read在异步io模式下是并发操作的。

1. 挂载目录
在 DB 节点上创建目标集合
首次启动时,需要在远程DB节点上创建映射的目标集合collection。后面挂载目录之后,mountpoint目录下的所有文件的实际内容会以lob的形式存放在该集合下。而所有文件的属性信息会分别存放在目录元数据集合和文件元数据集合中。
$sdbWelcome to SequoiaDB shell!help() for help, Ctrl+c or quit to exit> var db = new Sdb("localhost", 11810)Takes 0.124118s.> db.createCS("foo")localhost:11800.fooTakes 0.352408s.> db.foo.createCL("bar")localhost:11800.foo.barTakes 2.466226s.>
在FS节点上创建挂载目录及配置文件
挂载目录 mountpoint 为 FS 节点上的目录,用以挂载映射远程DB节点的目标集合,所以需要在FS节点上创建该目录。
启动 SequoiaFS 时可以指定从配置文件中读取配置参数,建议首次启动前创建配置文件并进行参数设置,配置文件及日志路径建议参考配置文件规则进行设置,以防止出现多次映射时互相覆盖的情况。
$mkdir -p opt/sequoiadb/mountpoint$mkdir -p opt/sequoiafs/conf/foo_bar/001/$mkdir -p opt/sequoiafs/log/foo_bar/001/
该例中按照默认参数值进行启动,所以不对参数进行配置,只是创建一个空配置文件,实际使用时按需写入相关配置值。
$touch opt/sequoiafs/conf/foo_bar/001/SequoiaFS.conf
挂载目录
$sequoiafs opt/sequoiadb/mountpoint -i localhost:11810 -l foo.bar --autocreate -c opt/sequoiafs/conf/foo_bar/001/ --diagpath opt/sequoiafs/log/foo_bar/001/ -o big_writes -o max_write=131072 -o max_read=131072
本地FS节点通过mount可以看到挂载信息
mount/dev/sda1 on type ext4 (rw,errors=remount-ro)proc on proc type proc (rw,noexec,nosuid,nodev)sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)none on /sys/fs/FUSE/connections type FUSEctl (rw)none on /sys/kernel/debug type debugfs (rw)none on /sys/kernel/security type securityfs (rw)udev on /dev type devtmpfs (rw,mode=0755)devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)none on /run/shm type tmpfs (rw,nosuid,nodev)SequoiaFS on /opt/sequoiadb/mountpoint type FUSE.SequoiaFS (rw,nosuid,nodev,user=sdbadmin)
在DB节点可以查看相关信息
var db = new Sdb("localhost", 11810)Takes 0.001705s.db.list(4){"Name": "SequoiaFS.maphistory"}{"Name": "SequoiaFS.sequenceid"}{"Name": "SequoiaFS.bar_dir148139183721030"}{"Name": "SequoiaFS.bar_file148139183721030"}{"Name": "foo.bar"}
对于每次mount,可以通过以上5张表查看相关信息,后续会介绍各表的作用,SequoiaFS.maphistory为映射挂载历史信息表,记录历史挂载的关键数据信息。
在FS节点挂载目录下创建文件和目录
cd /opt/sequoiadb/mountpoint/touch testfileecho 'hello, this is a testfile!' >> testfilecat testfilehello, this is a testfile!mkdir testdirlstestdir testfile
db.SequoiaFS.bar_dir148139183721030.find(){"_id": {"$oid": "5affae7115d4f9e718e723d0"},"Name": "testdir","Mode": 16877,"Uid": 2109,"Gid": 2000,"Pid": 1,"Id": 621,"NLink": 0,"Size": 4096,"CreateTime": 1526705777945,"ModifyTime": 1526705777945,"AccessTime": 1526705777945,"SymLink": ""}Return 1 row(s).Takes 0.019212s.
2. 卸载目录
fusermount 卸载
$fusermount -u /opt/sequoiadb/mountpoint
kill 进程
$ps -ef | grep sequoiafs$kill 程序PID
如果使用kill -9进行强杀进程,进程结束后会导致原 mountpoint 目录无法被 linux 文件系统正常访问的情况,需要使用fusermount -u <DIR> 来进行 unmount 即可。
总体来说,SequoiaDB 的 LOB 大对象存储通过与 FUSE 的结合,实现了对 Posix 文件系统的支持与兼容。用户在除了使用 SequoiaDB 的 LOB 标准 API 接口进行文件的存储外,还可以通过 SequoiaFS 文件挂载的方式进行文件数据的存储和查看。
往期技术干货







