可实现RSSD云硬盘120万IOPS的SPDK IO路径优化

一. 简介

用户对超高并发、超大规模计算等需求推动了存储硬件技术的不断发展,存储集群的性能越来越好,延时也越来越低,对整体IO路径的性能要求也越来越高。在云硬盘场景中,IO请求从生成到后端的存储集群再到返回之间的IO路径比较复杂,虚拟化IO路径尤其可能成为性能瓶颈,因为虚机内所有IO都需要通过它下发给后端的存储系统。我们使用了SPDK来优化虚拟化IO路径,提出了开源未解决的SPDK热升级和在线迁移方案,并且在高性能云盘场景中成功应用,取得了不错的效果,RSSD云硬盘最高可达120万IOPS。

二. SPDK vhost的基本原理

SPDK(Storage 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时虚机要使用大页内存,虚机在初始化时会将大页内存的信息发送给SPDK,SPDK会解析该信息并通过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 iodepth,1 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,索引号为1,2,3,4,5,6,SPDK按顺序的依次得到这个几个IO,并同时下发给设备处理,但实际可能请求1和4已经完成,并返回了成功了,如下图所示,而2,3,5,6都还没有完成。这个时候如果crash,重启后需要将2,3,5,6这个四个IO重新下发处理,而1和4是不能再次处理的,因为已经处理完成返回了,对应的内存也可能已经被释放。也就是说我们无法通过简单的扫描available ring来判断哪些IO需要重新下发,我们需要有一块内存来记录virtio vring中各个请求的状态,当重启后能够按照该内存中记录的状态来决定哪些IO是需要重新下发处理的,而且这块内存不能因SPDK重启而丢失,那么显然使用qemu进程的内存是最合适的。所以我们在qemu中针对每个virtio vring申请一块共享内存,在初始化时发送给SPDK,SPDK在处理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的代码甚至默认都没有开放编译,我们需要做些修改:

  1. 安装最新的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作为一个快速发展迭代的项目,每个版本都会给我们带来惊喜,里面也有很多有意思的功能等待我们发掘并进一步运用到云盘及其它产品性能的提升上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/234241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】网络编程-UDP字典服务器客户端简单代码编写

上文讲了UDP回响服务器客户端简单代码编写 本文将讲述UDP字典服务器客户端简单代码编写。所谓回显,就是指客户端向服务器发送一个报文,从服务器那里得到一条一模一样的回响报文 而我们的字典功能呢,则是实现了输入中文,得到对应…

智能物联网汽车3d虚拟漫游展示增强消费者对品牌的认同感和归属感

汽车3D虚拟展示系统是一种基于web3D开发建模和VR虚拟现实技术制作的360度立体化三维汽车全景展示。它通过计算机1:1模拟真实的汽车外观、内饰和驾驶体验,让消费者在购车前就能够更加深入地了解车辆的性能、特点和设计风格。 华锐视点云展平台是一个专业的三维虚拟展…

Pytorch:backward()函数详解

.backward() .backward() 是 PyTorch 中用于自动求导的函数,它的主要作用是计算损失函数对模型参数的梯度,从而实现反向传播算法。 在深度学习中,我们通常使用梯度下降算法来更新模型参数,使得模型能够逐步逼近最优解。 在梯度下…

linux中deadline调度原理与代码注释

简介 deadline调度是比rt调度更高优先级的调度,它没有依赖于优先级的概念,而是给了每个实时任务一定的调度时间,这样的好处是:使多个实时任务场景的时间分配更合理,不让一些实时任务因为优先级低而饿死。deadline调度…

(保姆级教程)一篇文章,搞定所有Linux命令,以及tar解压缩命令,wget、rpm等下载安装命令,Linux的目录结构,以及用户和用户组

文章目录 Linux命令1. Linux目录结构2. 基本命令(了解)3. 目录(文件夹)命令列出目录切换目录创建目录删除目录复制目录移动和重命名目录 4. 文件命令创建文件编辑文件编辑文件时的其他操作 查看文件移动/重命名文件复制文件删除文…

机器学习---聚类(原型聚类、密度聚类、层次聚类)

1. 原型聚类 原型聚类也称为“基于原型的聚类” (prototype-based clustering),此类算法假设聚类结构能通过一 组原型刻画。算法过程:通常情况下,算法先对原型进行初始化,再对原型进行迭代更新求解。著 名的原型聚类算法&#…

基于Redis限流(aop切面+redis实现“令牌桶算法”)

令牌桶算法属于流量控制算法,在一定时间内保证一个键(key)的访问量不超过某个阈值。这里的关键是设置一个令牌桶,在某个时间段内生成一定数量的令牌,然后每次访问时从桶中获取令牌,如果桶中没有令牌&#x…

【机器学习】梯度下降法:从底层手写实现线性回归

【机器学习】Building-Linear-Regression-from-Scratch 线性回归 Linear Regression0. 数据的导入与相关预处理0.工具函数1. 批量梯度下降法 Batch Gradient Descent2. 小批量梯度下降法 Mini Batch Gradient Descent(在批量方面进行了改进)3. 自适应梯度…

C++相关闲碎记录(17)

1、IO操作 (1)class及其层次体系 (2)全局性stream对象 (3)用来处理stream状态的成员函数 前四个成员函数可以设置stream状态并返回一个bool值,注意fail()返回是failbit或者badbit两者中是否任一…

【重点】【DP】152.乘积最大的子数组

题目 法1:DP 参考:https://blog.csdn.net/Innocence02/article/details/128326633 f[i]表示以i结尾的连续子数组的最大乘积,d[i]表示以i结尾的连续子数组的最小乘积 。 如果只有正数,我们只需要考虑最大乘积f[i];有负…

MATLAB - Gazebo 仿真环境

系列文章目录 前言 机器人系统工具箱(Robotics System Toolbox™)为使用 Gazebo 模拟器可视化的模拟环境提供了一个界面。通过 Gazebo,您可以在真实模拟的物理场景中使用机器人进行测试和实验,并获得高质量的图形。 Gazebo 可在…

c# OpenCV 基本绘画(直线、椭圆、矩形、圆、多边形、文本)(四)

我们将在这里演示如何使用几何形状和文本注释图像。 Cv2.Line() 绘制直线 Cv2.Ellipse() 绘制椭圆Cv2.Rectangle() 绘制矩形Cv2.Circle() 绘制圆Cv2.FillPoly() 绘制多边形Cv2.PutText() 绘制文本 一、绘制直线 Cv2.Line(image, start_point, end_point, color, thickness) …

从传统型数据库到非关系型数据库

一 什么是数据库 数据库顾名思义保存数据的仓库,其本质是一个具有数据存储功能的复杂系统软件,数据库最终把数据保存在计算机硬盘,但数据库并不是直接读写数据在硬盘,而是中间隔了一层操作系统,通过文件系统把数据保存…

2023ChatGPT浪潮,2024开源大语言模型会成王者?

《2023ChatGPT浪潮,2024开源大语言模型会成王者?》 一、2023年的回顾 1.1、背景 我们正迈向2023年的终点,回首这一年,技术行业的发展如同车轮滚滚。尽管互联网行业在最近几天基本上处于冬天,但在这一年间我们仍然经…

递归经典三题

目录1.斐波那契数列: 2.青蛙跳台阶问题: 3.汉诺塔问题 1.斐波那契数列: 由斐波那契数列从第三项开始,每一项等于前两项之和,可以使用递归计算给定整数的斐波那契数。 1,1,2,3&am…

酒水品牌网站建设的效果如何

酒是人们餐桌常常出现的饮品,市场中的大小酒品牌或经销商数量非常多,国内国外都有着巨大市场,酒讲究的是品质与品牌,信息发展迅速的时代,商家们都希望通过多种方式获得生意增长。 酒商非常注重品牌,消费者也…

为什么要编写测试用例,测试用例写给谁看?

“为什么要编写测试用例,测试用例写给谁看”,这个问题看似简单,但却涵盖了一系列复杂的考虑因素,并不太好回答。 为了向各位学测试的同学们解释清楚“为什么编写测试用例是至关重要的”,我将通过以下5个方面进行展开&…

EMD、EEMD、FEEMD、CEEMD、CEEMDAN的区别、原理和Python实现(二)EEMD

往期精彩内容: 风速预测(一)数据集介绍和预处理-CSDN博客 风速预测(二)基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测(三)EMD-LSTM-Attention模型-CSDN博客 风速预测(四&#xf…

鸿蒙 - arkTs:渲染(循环 - ForEach,判断 - if)

ForEach循环渲染: 参数: 要循环遍历的数组,Array类型遍历的回调方法,Function类型为每一项生成唯一标识符的方法,有默认生成方法,非必传 使用示例: interface Item {name: String,price: N…

作物模型中引入灌溉参数

在没有设置灌溉时,土壤水分模拟结果如下找到了PCSE包中田间管理文件的标准写法 在agromanager.py中有详细的信息(如何设置灌溉以及施肥量) Version: 1.0 AgroManagement: - 2022-10-15:CropCalendar:crop_name: sugar-beetvariety_name: