点击上方蓝字,关注我们吧
作者:陈旭 chenxu14@meituan.com
01
背景说明
rocksdb是一个被广泛采用的KV系统,其功能已经逐渐演变成很多上层应用的一个基础组件,像Ceph的bluestore,nebula的点边存储,还有tikv系统,底层都是依赖rocksdb来做数据存储或元数据管理的,因此rocksdb的吞吐能力表现对上层应用系统的能力建设可以起到非常重要的一环。
随着分布式系统开始陆续上云,计算与存储相分离的体系架构也开始广泛得到部署应用,在该体系模式之下存储节点与计算节点往往对物理资源有着不同的需求纬度。存储节点比较侧重磁盘以及带宽,而计算节点则更侧重CPU资源。如果我们能够通过较少的CPU来跑满整个磁盘IO上限,那么便可以预留出更多的CPU资源去服务于计算节点。
基于NVMe设备,一种有效改善吞吐能力的办法是借助内核旁路机制,通过用户态的驱动程序来与底层的NVMe设备进行直接的交互,从而避免系统调用所带来的overhead开销。为此SPDK应运而生,其不但提供了一个用户态的文件系统(blobfs),还将磁盘驱动程序放到了用户态空间,使得数据的访问可以在用户态直接进行,这样便有效避免了操作系统的上下文切换以及数据在用户态和内核态之间的拷贝传递。
得益于rocksdb对Env的灵活封装,使得底层的数据存储几乎可以是任何的文件系统,包括blobfs。然而通过benchmark我们发现,在某些特定的workload场景下,blobfs的性能表现并没有达到预期的那样理想,比如在对readrandom测试过程中,发现无论是请求时延还是吞吐能力,其表现都是不如内核态的。
为此,我们对blobfs内部的工作流程做了一些调研和梳理,通过分析其内部的执行逻辑来寻找可做改善的性能空间。
02
BLOBFS改造 - 多IO Channel支持
blobfs原生的线程工作模型如上图左侧所示:首先其内部会创建两个不同的io_device(blobfs_md和blobfs_sync)来应对不同类型的IO请求(严格意义上讲是三个,只不过blobfs_io尚未投入使用),如果请求涉及元数据的访问操作(eg. open, close, create, rename),需要采用blobfs_md所创建的io_channel,其他情况使用blobfs_sync所创建的io_channel即可。io_channel是blobfs暴露给上层应用的两个通信管道,相当于是交互的入口,然而其本身并不是线程安全的,不同的线程没有办法同时共用同一个io_channel来与blobfs进行交互,只能供单线程内部调用使用。所以SPDK在功能实现上将其与reactor_0进行了绑定,只有reactor_0所对应的线程可以与blobfs做交互,而其他线程或者reactor则需要通