打造用户态存储利器,基于SPDK的存储引擎Blobstore & BlobFS

作者简介:周雁波,Intel存储软件工程师,主要从事SPDK软件开发工作。

转载自DPDK与SPDK开源社区

Blobstore是位于SPDK bdev之上的Blob管理层,用于与用户态文件系统Blobstore Filesystem (BlobFS)集成,从而代替传统的文件系统,支持更上层的服务,如数据库MySQL、K-V存储引擎Rocksdb以及分布式存储系统Ceph、Cassandra等。以Rocksdb为例,通过BlobFS作为Rocksdb的存储后端的优势在于,I/O经由BlobFS与Blobstore下发到bdev,随后由SPDK用户态driver写入磁盘。整个I/O流从发起到落盘均在用户态操作,完全bypass内核。此外,可以充分利用SPDK所提供的异步、无锁化、Zero Copy、轮询等机制,大幅度减少额外的系统开销。它们之间的关系如下所示(以NVMe bdev为例):

BlobFS在管理文件时,主要依赖于Blobstore对blob的分配与管理。Blob类似于文件的概念,而又不完全等同于文件,其并不支持所有文件的POSIX接口。BlobFS与Blobstore的关系可以理解为Blobstore实现了对Blob的管理,包括Blob的分配、删除、读取、写入、元数据的管理等,而BlobFS是在Blobstore的基础上进行封装的一个轻量级文件系统,用于提供部分对于文件操作的接口,并将对文件的操作转换为对Blob的操作,BlobFS中的文件与Blobstore中的Blob一一对应。在Blobstore下层,与SPDK bdev层对接。SPDK bdev层类似于内核中的通用块设备层,是对底层不同类型设备的统一抽象管理,例如NVMe bdev、Malloc bdev、AIO bdev等。

Blobstore中结构的划分

在blobstore中,将SSD中的块划分为多个抽象层,主要由Logical Block、Page、Cluster、Blob组成,它们之间的关系如下所示:

Logical Block:与块设备中所提供的逻辑块相对应,通常为512B或4KiB。

Page:由多个连续的Logical Block构成,通常一个page的大小为4KiB,因此一个Page由八个或一个Logical Block构成,取决于Logical Block的大小。在Blobstore中,Page是连续的,即从SSD的LBA 0开始,多个或一个块构成Page 0,接下来是Page 1,依次类推。

Cluster:由多个连续的Page构成,通常一个Cluster的大小默认为1MiB,因此一个Cluster由256个Page构成。Cluster与Page一样,是连续的,即从SSD的LBA 0开始的位置依次为Cluster 0到Cluster N。

Blob:Blobstore中主要的操作对象为Blob,与BlobFS中的文件相对应,提供read、write、create、delete等操作。一个Blob由多个Cluster构成,但构成Blob中的Cluster并不一定是连续的。

那么Blobstore是如何管理块的分配呢?

在Blobstore中,会将cluster 0作为一个特殊的cluster。该cluster用于存放Blobtore的所有信息以及元数据,对每个blob数据块的查找、分配都是依赖cluster 0中所记录的元数据所进行的。Cluster 0的结构如下:

Cluster 0中的第一个page作为super block,Blobstore初始化后的一些基本信息都存放在super block中,例如cluster的大小、已使用page的起始位置、已使用page的个数、已使用cluster的起始位置、已使用cluster的个数、Blobstore的大小等信息。

Cluster 0中的其它page将组成元数据域(metadata region)。元数据域主要由以下几部分组成:

Metadata Page Allocation:用于记录所有元数据页的分配情况。在分配或释放元数据页后,将会对metadata page allocation中的数据做相应的修改。

Cluster Allocation:用于记录所有cluster的分配情况。在分配新的cluster或释放cluster后会对cluster allocation中的数据做相应的修改。

Blob Id Allocation:用于记录blob id的分配情况。对于blobstore中的所有blob,都是通过唯一的标识符blob id将其对应起来。在元数据域中,将会在blob allocation中记录所有的blob id分配情况。

Metadata Pages Region:元数据页区域中存放着每个blob的元数据页。每个blob中所分配的cluster都会记录在该blob的元数据页中,在读写blob时,首先会通过blob id定位到该blob的元数据页,其次根据元数据页中所记录的信息,检索到对应的cluster。对于每个blob的元数据页,并不是连续的。

对于一个blob来说,metadata page记录了该blob的所有信息,数据存放于分配给该blob的cluster中。在创建blob时,首先会为其分配blob id以及metadata page,其次更新metadata region。当对blob进行写入时,首先会为其分配cluster,其次更新该blob的metadata page,最后将数据写入,并持久化到磁盘中。

为了实现对磁盘空间的动态分配管理,Blobstore中为每个blob分配的cluster并不是连续的。对于每个blob,通过相应的结构维护当前使用的cluster以及metadata page的信息:clusters与pages。Cluster中记录了当前该blob所有cluster的LBA起始地址,pages中记录了当前该blob所有metadata page的LBA起始地址。

Blobstore实现了对磁盘空间分配的动态管理,并保证断电不丢失数据,因此Blob具有persistent特性。Blobstore中的配置信息与数据信息均在super block与metadata region中管理,在重启后,若要保持persistent,可以通过Blobstore中所提供的load操作。

注意:Blob的persistent主要是针对NVMe这类bdev。对于Malloc bdev,由于其本身的性质,是无法保证Blob的persistent,需要重启后进行重新配置。

下面通过文件的读写来讲解BlobFS与Blobstore中的I/O流程:

文件读取:文件读取操作的流程图如下所示:

为了提高文件的读取效率,BlobFS在内存中提供了cache buffer。在文件读写时,首先会进行read ahead操作,将一部分数据从磁盘预先读取到内存的buffer中。其后,根据cache buffer的大小,对文件的I/O进行切分,使每个I/O的最大长度不超过一个cache buffer的大小。对于拆分后的文件I/O,会根据其offset在cache buffer tree中查找相应的buffer。若存在,则直接从cache buffer中读取数据,进行memcpy。而对于没有缓存到cache buffer中的数据,将会对该文件的读取,转换到该文件对应的Blob进行读取。对Blob读取时候,根据已打开的blob结构中记录的信息,可以获取该blob所有cluster的LBA起始位置,并根据读取位置的offset信息,计算相应的LBA地址。最后向SPDK bdev层发送异步的读请求,并等待I/O完成。BlobFS所提供的读操作为同步读,I/O完成后会在callback函数中,通过信号量通知BlobFS完成信号,至此文件读取结束。

对于cache buffer tree,其结构如下所示:

Cache buffer tree是由多层树结构组成。最底层Level 0叶子节点为buffer node,是用于存放数据的buffer。Level 0以上的其它层中,均为tree node,用于构建树的索引结构。在文件读写的时候,根据文件结构中的根节点以及读取位置的offset信息,在树结构中通过索引查找buffer node的位置,即从Level N,逐步定位到对应的Level 0的叶子节点。

文件写入:文件写入操作的流程图如下所示:

BlobFS目前用于支持上层的Rocksdb,在Rocksdb的抽象环境层中提供文件的接口,目前仅支持append类型的写操作。在进行文件写入时,首先会根据文件当前的写入位置检查是否符合cache buffer写入需求,若满足,则直接将数据写入到cache buffer中,同时触发异步的flush操作。在flush的过程中,BlobFS触发Blob的写操作,将cache buffer中的数据,写入到文件对应blob的相应位置。若不满足cache buffer的写入需求,BlobFS则直接触发文件对应的blob的写操作。Blobstore首先为该blob分配cluster,根据计算得到的写入LBA信息,向SPDK bdev层发送异步的写请求,将数据写入,并更新相应的元数据。对于元数据的更新,出于性能考虑,当前对元数据的更新都在内存中操作,当用户使用强制同步或卸载Blobstore时,更新后的元数据信息才会同步到磁盘中。此外,blob结构中维护了两份可变信息(指cluster与metadata page)的元数据,分别为clean与active。Clean中记录的是当前磁盘的元数据信息,而active中记录的是当前在内存中更新后的元数据信息。同步操作会将clean中记录的信息与active记录的信息相匹配。

总结

Blobstore实现对Blob管理,Blob类似与文件的概念,但又不完全等同于文件,Blob没有完全遵循文件的POSIX接口,因此避免与文件混淆,在SPDK中称之为Blob而不是File。Blobstore Filesystem (BlobFS)是基于Blobstore实现的轻量级文件系统,对Blobstore进行封装,提供一些文件的常用接口,如read、write、open、sync等,其目的在于作为文件系统支持更上层的应用,例如Rocksdb。但其本质仍然是Blobstore,因此命名为BlobFS。目前SPDK基于维护了Rocksdb的一个分支,该分支下的Rocksdb在环境抽象层主要通过BlobFS进行对接,I/O可以经由BlobFS绕过内核I/O栈。关于Rocksdb的搭建与测试步骤,可以参考[3]。

参考链接:
[1] https://spdk.io
[2] https://github.com/spdk/spdk
[3] https://spdk.io/doc/blobfs.html


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/22880.html
分享到:
相关文章
条评论

登录后才可以评论

SDNLAB君 发表于19-01-15
1