网站建设作业,google官方下载,微信开发者工具的介绍,行业网站开发程序一. 简介
用户对超高并发、超大规模计算等需求推动了存储硬件技术的不断发展#xff0c;存储集群的性能越来越好#xff0c;延时也越来越低#xff0c;对整体IO路径的性能要求也越来越高。在云硬盘场景中#xff0c;IO请求从生成到后端的存储集群再到返回之间的IO路径比较…一. 简介
用户对超高并发、超大规模计算等需求推动了存储硬件技术的不断发展存储集群的性能越来越好延时也越来越低对整体IO路径的性能要求也越来越高。在云硬盘场景中IO请求从生成到后端的存储集群再到返回之间的IO路径比较复杂虚拟化IO路径尤其可能成为性能瓶颈因为虚机内所有IO都需要通过它下发给后端的存储系统。我们使用了SPDK来优化虚拟化IO路径提出了开源未解决的SPDK热升级和在线迁移方案并且在高性能云盘场景中成功应用取得了不错的效果RSSD云硬盘最高可达120万IOPS。
二. SPDK vhost的基本原理
SPDKStorage Performance Development Kit )提供了一组用于编写高性能、可伸缩、用户态存储应用程序的工具和库基本组成分为用户态、轮询、异步、无锁 NVMe 驱动提供了从用户空间应用程序直接访问SSD的零拷贝、高度并行的访问。
在虚拟化IO路径中virtio是比较常用的一种半虚拟化解决方案而virtio底层是通过vring来通信下面先介绍下virtio vring的基本原理每个virtio vring 主要包含了以下几个部分 desc table数组该数组的大小等于设备的队列深度一般为128。数组中每一个元素表示一个IO请求元素中会包含指针指向保存IO数据的内存地址、IO的长度等基本信息。一般一个IO请求对应一个desc数组元素当然也有IO涉及到多个内存页的那么就需要多个desc连成链表来使用未使用的desc元素会通过自身的next指针连接到free_head中形成一个链表以供后续使用。available数组该数组是一个循环数组每一项表示一个desc数组的索引当处理IO请求时从该数组里拿到一个索引就可以到desc数组里面找到对应的IO请求了。used 数组该数组与avail类似只不过用来表示完成的IO请求。当一个IO请求处理完成时该请求的desc数组索引就会保存在该数组中而前端virtio驱动得到通知后就会扫描该数据判断是否有请求完成如果完成就会回收该请求对应的desc数组项以便下个IO请求使用。
SPDK vhost的原理比较简单初始化时先由qemu的vhost驱动将以上virtio vring数组的信息发送给SPDK然后SPDK通过不停的轮寻available数组来判断是否有IO请求有请求就处理处理完后将索引添加到used数组中并通过相应的eventfd通知virtio前端。
当SPDK收到一个IO请求时只是指向该请求的指针在处理时需要能直接访问这部分内存而指针指向的地址是qemu地址空间的显然不能直接使用因此这里需要做一些转化。
在使用SPDK时虚机要使用大页内存虚机在初始化时会将大页内存的信息发送给SPDKSPDK会解析该信息并通过mmap映射同样的大页内存到自己的地址空间这样就实现了内存的共享所以当SPDK拿到qemu地址空间的指针时通过计算偏移就可以很方便的将该指针转换到SPDK的地址空间。
由上述原理我们可以知道SPDK vhost通过共享大页内存的方式使得IO请求可以在两者之间快速传递这个过程中不需要做内存拷贝完全是指针的传递因此极大提升了IO路径的性能。
我们对比了原先使用的qemu云盘驱动的延时和使用了SPDK vhost之后的延时为了单纯对比虚拟化IO路径的性能我们采用了收到IO后直接返回的方式
1.单队列1 iodepth, 1 numjob
qemu 网盘驱动延时 SPDK vhost延时 可见在单队列情况下延时下降的非常明显平均延时由原来的130us下降到了7.3us。
2.多队列128 iodepth1 numjob
qemu 网盘驱动延时 SPDK vhost延时 多队列时IO延时一般会比单队列更大些可见在多队列场景下平均延时也由3341us下降为1090us下降为原来的三分之一。
三. SPDK热升级
在我们刚开始使用SPDK时发现SPDK缺少一重要功能——热升级。我们使用SPDK 并基于SPDK开发自定义的bdev设备肯定会涉及到版本升级并且也不能100%保证SPDK进程不会crash掉因此一旦后端SPDK重启或者crash前端qemu里IO就会卡住即使SPDK重启后也无法恢复。
我们仔细研究了SPDK的初始化过程发现在SPDK vhost启动初期qemu会下发一些配置信息而SPDK重启后这些配置信息都丢失了那么这是否意味着只要SPDK重启后重新下发这些配置信息就能使SPDK正常工作呢我们尝试在qemu中添加了自动重连的机制并且一旦自动重连完成就会按照初始化的顺序再次下发这些配置信息。开发完成后初步测试发现确实能够自动恢复但随着更严格的压测发现只有在SPDK正常退出时才能恢复而SPDK crash退出后IO还是会卡住无法恢复。从现象上看应该是部分IO没有被处理所以qemu端虚机一直在等待这些IO返回导致的。
通过深入研究virtio vring的机制我们发现在SPDK正常退出时会保证所有的IO都已经处理完成并返回了才退出也就是所在的virtio vring中是干净的。而在意外crash时是不能做这个保证的意外crash时virtio vring中还有部分IO是没有被处理的所以在SPDK恢复后需要扫描virtio vring将未处理的请求下发下去。这个问题的复杂之处在于virtio vring中的请求是按顺序下发处理的但实际完成的时候并不是按照下发的顺序的。
假设在virtio vring的available ring中有6个IO索引号为123456SPDK按顺序的依次得到这个几个IO并同时下发给设备处理但实际可能请求1和4已经完成并返回了成功了如下图所示而2356都还没有完成。这个时候如果crash重启后需要将2356这个四个IO重新下发处理而1和4是不能再次处理的因为已经处理完成返回了对应的内存也可能已经被释放。也就是说我们无法通过简单的扫描available ring来判断哪些IO需要重新下发我们需要有一块内存来记录virtio vring中各个请求的状态当重启后能够按照该内存中记录的状态来决定哪些IO是需要重新下发处理的而且这块内存不能因SPDK重启而丢失那么显然使用qemu进程的内存是最合适的。所以我们在qemu中针对每个virtio vring申请一块共享内存在初始化时发送给SPDKSPDK在处理IO时会在该内存中记录每个virtio vring请求的状态并在意外crash恢复后能利用该信息找出需要重新下发的请求。 四. SPDK在线迁移
SPDK vhost所提供的虚拟化IO路径性能非常好那么我们有没有可能使用该IO路径来代替原有的虚拟化IO路径呢我们做了一些调研SPDK在部分功能上并没有现有的qemu IO路径完善其中尤为重要的是在线迁移功能该功能的缺失是我们使用SPDK vhost代替原有IO路径的最大障碍。
SPDK在设计时更多是为网络存储准备的所以支持设备状态的迁移但并不支持设备上数据的在线迁移。而qemu本身是支持在线迁移的包括设备状态和设备上的数据的在线迁移但在使用vhost模式时是不支持在线迁移的。主要原因是使用了vhost之后qemu只控制了设备的控制链路而设备的数据链路已经托管给了后端的SPDK也就是说qemu没有设备的数据流IO路径所以并不知道一个设备那些部分被写入了。
在考察了现有的qemu在线迁移功能后我们觉着这个技术难点并不是不能解决的因此我们决定在qemu里开发一套针对vhost存储设备的在线迁移功能。
块设备的在线迁移的原理比较简单可以分为两个步骤第一个步骤将全盘数据从头到尾拷贝到目标虚机因为拷贝过程时间较长肯定会发生已经拷贝的数据又被再次写入的情况这个步骤中那些再次被写脏的数据块会在bitmap中被置位留给第二个步骤来处理步骤二中通过bitmap来找到那些剩余的脏数据块将这些脏数据块发送到目标端最后会block住所有的IO然后将剩余的一点脏数据块同步到目标端迁移就完成了。
SPDK的在线迁移原理上于上面是相同的复杂之处在于qemu没有数据的流IO路径所以我们在qemu中开发了一套驱动可以用来实现迁移专用的数据流IO路径并且通过共享内存加进程间互斥的方式在qemu和SPDK之间创建了一块bitmap用来保存块设备的脏页数量。考虑到SPDK是独立的进程可能会出现意外crash的情况因此我们给使用的pthread mutex加上了PTHREAD_MUTEX_ROBUST特性来防止意外crash后死锁的情况发生整体架构如下图所示 五. SPDK IO uring体验
IO uring是内核中比较新的技术在上游内核5.1以上才合入该技术主要是通过用户态和内核态共享内存的方式来优化现有的aio系列系统调用使得提交IO不需要每次都进行系统调用这样减少了系统调用的开销从而提供了更高的性能。
SPDK在最新发布的19.04版本已经包含了支持uring的bdev但该功能只是添加了代码并没有开放出来当然我们可以通过修改SPDK代码来体验该功能。
首先新版本SPDK中只是包含了io uring的代码甚至默认都没有开放编译我们需要做些修改
安装最新的liburing库同时修改spdk的config文件打开io uring的编译
2. 添加针对io uring设备的rpc调用使得我们可以像创建其他bdev设备那样创建出io uring的设备
3. 最新的liburing已经将io_uring_get_completion调用改成了io_uring_peek_cqe并需要配合io_uring_cqe_seen使用所以我们也要调整下SPDK中io uring的代码实现避免编译时出现找不到io_uring_get_completion函数的错误 4. 使用修改open调用使用O_SYNC模式打开文件确保我们在数据写入返回时就落地了并且比调用fdatasync效率更高我们对aio bdev也做了同样的修改同时添加读写模式 经过上述修改spdk io uring设备就可以成功创建出来了我们做下性能的对比
使用aio bdev的时候 使用io uring bdev的时候 可见在最高性能和延时上 io uring都有不错的优势IOPS提升了约20%延迟降低约10%。这个结果其实受到了底层硬件设备最大性能的限制还未达到io uring的上限。
六. 总结
SPDK技术的应用使得虚拟化IO路径的性能提升不再存在瓶颈也促使UCloud高性能云盘产品可以更好的发挥出后端存储的性能。当然一项技术的应用并没有那么顺利SPDK作为一个快速发展迭代的项目每个版本都会给我们带来惊喜里面也有很多有意思的功能等待我们发掘并进一步运用到云盘及其它产品性能的提升上。