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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

less基础介绍

什么是less? Less是一个C5S预处理器,Less文件后缀是,less。扩充了 CSS 语言,使CSS 具备一定的逻辑性、计算能力 注意事项: 浏览器不识别 Less 代码,目前阶段,网页要引入对应的 CSS 文件 VS Code 插件: Easy LESS,保存 …

JVM快速入门

JVM 字节码 字节码文件的组成 字节码由五个部分组成:基础信息 常量池 字段 方法 属性 基础信息: 魔数、字节码文件对应的版本号、访问标识(public final)、该类的父类索引、该类实现哪些接口的索引 魔数:文件无法…

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

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

ValueError: source code string cannot contain null bytes

解决:把存在这个问题的包,全部卸载重装 pip uninstall xxx pip install xxx

美国联邦机动车安全标准-FMVSS

FMVSS标准介绍: FMVSS是美国《联邦机动车安全标准》,由美国运输部下属的国家公路交通安全管理局(简称NHTSA)具体负责制定并实施。是美国联邦政府针对机动车制定的安全标准,旨在提高机动车的安全性能,减少交通事故中的人员伤亡。F…

路由跳转传递参数注意事项,查询字符串传参,params传参需要注意的地方,菜单内容的二级内容 vue3

路由跳转和传参(vue3)_vue3路由传参-CSDN博客 注意: import {useRouter} from "vue-router"const routeruseRouter()1.查询字符串传参,传一个对象,对象里面可以写path字段 router.push({path:/item,query:{id:1}} ) 通过当前路由…

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

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

如何通过ETLCloud的API对接功能实现各种SaaS平台数据对接

前言 当前使用SaaS系统的企业越来越多,当我们需要对SaaS系统中产生的数据进行分析和对接时就需要与SaaS系统提供的API进行对接,因为SaaS一般是不会提供数据库表给企业,这时就应该使用ETL(Extract, Transform, Load)的…

Jmeter接口程序项目实战教程

1.什么是jmeter? JMeter是100%完全由Java语言编写的,免费的开源软件,是非常优秀的性能测试和接口测试工具,支持主流协议的测试 2.jmeter能做什么? JMeter是100%完全由Java语言编写的软件性能测试的GUI的测试工具&am…

车载蓝牙物联网解决方案

车载蓝牙物联网解决方案是一种基于蓝牙技术,结合物联网技术的智能车载系统。它利用蓝牙技术将智能手机、智能手表、智能车载设备等连接起来,实现设备之间的无缝通信和数据共享,为驾驶者提供更加便捷、安全和智能的驾驶体验。 车载蓝牙物联网解…

【3D数据读取】利用JAVA读取GLB(GLTF)文件数据

了解GLB和GLTF: GLB和GLTF是用于共享3D数据的标准化文件格式。GLB是GLTF的二进制格式,而GLTF基于JSON,一种基于文本的数据格式。 GLB文件: 由一个头部和一个二进制数据块组成。头部包含文件的元数据,例如文件版本、文件…

网络时代的新宠

当今社会,随着科技的不断进步和互联网的普及,手机已经成为了人们生活中不可或缺的一部分。它不仅仅是一个通信工具,更是娱乐、学习和获取信息的利器。而其中,手机无人直播更是近年来备受关注的热门话题。 直播,一种实…

hive 用户自定义函数udf,udaf,udtf

udf:一对一的关系 udtf:一对多的关系 udaf:多对一的关系 使用Java实现步骤 自定义编写UDF函数注意: 1.需要继承org.apache.hadoop.hive.ql.exec.UDF 2.需要实现evaluete函数 编写UDTF函数注意: 1.需要继承org.apache…

【MongoDB】--MongoDB的Sort排序问题

目录 一、问题背景描述1.1、问题背景1.2、问题分析 二、建立索引支持深度翻页查询2.1、调整sort排序的内存限制【不建议】2.2、创建索引2.3、拓展--组合索引什么时候失效 二、聚合查询解决深度翻页查询 一、问题背景描述 1.1、问题背景 现实系统页面翻页到20000页之后&#x…

MQTT直连接入

本文介如绍何使用MQTT协议,将设备直连到平台内置的MQTT服务。 操作步骤 创建产品 物联网->设备管理->选择产品,填写产品基础信息。 参数 对应设备侧参数 ID 产品唯一标识,若不填写,系统将自动生成唯一ID 设备类型 直…

[Linux] LVS+Keepalived高可用集群部署

一、Keepalived实现原理 1.1 高可用方案 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案,可以解决静态路由出现的单点故障问题。 在一个LVS服务集群中通常有主服务器(MASTER)和备份服务器(BACKUP)两种角色…

【qt信号槽-5】信号槽相关注意事项记录

背景: 信号槽是qt很重要的概念,遇到问题帮助没少看。其中就有signals and slots这一章节,说得很到位。 概念琐碎,记录备忘。不对之处望指正。 【qt信号槽-1】槽函数重写问题,qt_metacall和qt_static_metacall-CSDN博…

12.19力扣

1901. 寻找峰值 II 题目描述:   一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。   给你一个 从 0 开始编号 的 m x n 矩阵 mat ,其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值 mat[i][j] 并 返回其位置 […

docker-compose部署容器可视化管理平台portainer

一、安装docker docker--安装docker-ce-CSDN博客 二、安装docker-compose 安装docker-compose-CSDN博客 三、docker-compose部署portainer yml文件,需要开放9000端口 [rootlgb /]# vi /opt/docker-compose-yml/portainer/docker-compose.yml version: "…

基于JAVA的海南旅游景点推荐系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…