直接在虚拟机里跑fio,ioengine选libaio是默认底线,追求极限性能或测试新内核就用io_uring;两者性能差距在多数虚拟化场景下约为5%-15%,但选错engine可能让测试结果完全失真。这个结论基于近年大量云服务器存储性能测试的行业共识,下面拆开讲清楚每个engine的脾气和适用场景。
虚拟机里fio ioengine的三大主流选择
fio的ioengine决定它怎么向操作系统发起读写请求,虚拟机环境下,用户最常见的三个选择是sync、libaio和io_uring。
- sync:最朴素的同步读写模式,每个I/O请求发出去之后,线程就卡在那儿等内核回应,一次只处理一个请求,优点是实现简单,兼容性极好;缺点是并发能力极差,队列深度上不去,在SSD和高性能存储上完全发挥不出硬件实力。
- libaio:Linux原生异步I/O接口,经典中的经典,它允许fio一次性提交一批读写请求,然后批量收割结果,不需要每个请求都等内核反馈,libaio配合direct=1(绕过页缓存直写盘)能压出存储设备的真实极限,这是绝大多数云厂商压测PPS和IOPS的标准组合。
- io_uring:Linux 5.1引入的新一代异步I/O框架,近年来在存储测试圈里风头正劲,它用共享内存环形队列减少系统调用开销,同时支持缓冲和非缓冲模式,还具备轮询(polling)能力,能进一步压低延迟。
另有部分用户会用到pvsync2或posixaio,但它们在虚拟机下的表现不如前三者稳定,配置复杂度过高,不太建议在生产环境测试中选用。
为什么虚拟机下不能随便选ioengine
物理机上习惯用libaio的用户,到了虚拟机环境经常会遇到一个尴尬现象:明明宿主机的SSD很强,但虚拟机里跑出来的随机读IOPS低得吓人,甚至不如本地机械硬盘的指标。
这里面的核心问题是虚拟化层对异步I/O的处理方式和物理机完全不同,KVM/QEMU默认通过virtio-blk或virtio-scsi给虚拟机暴露存储设备,虚拟机内部的libaio请求发出来后,要先经过virtio队列转入宿主机QEMU进程,再由宿主机内核的native AIO或线程池真正落盘,多一层转发意味着多一层排队,也意味着异步模型的效率会被打破。
- 如果宿主机QEMU配置了
aio=native的参数,libaio的请求还能保持较好的异步特性。 - 但如果宿主机QEMU用的是线程池模式(即
aio=threads),libaio请求实际上会被拆成多个同步线程来处理,那跟sync就没什么本质区别了。
所以业内专家指出,判断虚拟机里fio ioengine选哪个,要先搞清楚虚拟化平台的底层存储架构,如果QEMU的配置不可控(比如用公有云),那io_uring的优势反而会凸显出来,因为io_uring具备内核级轮询能力,能减少virtio队列带来的上下文切换损耗,对高IOPS场景的补偿效果更明显。
实测视角:io_uring与libaio的性能差异
测试环境不同,结果差异很大,以常见的KVM虚拟机搭配virtio-blk、后端为NVMe SSD的场景,实际操作步骤和结果如下。
测试机基础配置:
- 宿主机:Linux Kernel 5.15,KVM虚拟化
- 虚拟机:4 vCPU / 8GB内存,Ubuntu 22.04,内核版本5.15
- 存储设备:virtio-blk挂载,队列深度设置为32
测试命令:分别用libaio和io_uring跑同一个随机读测试,参数保持一致。
# libaio模式
fio --name=randread-libaio --ioengine=libaio --rw=randread --bs=4k --iodepth=32
--direct=1 --size=4G --numjobs=1 --runtime=30 --group_reporting
# io_uring模式
fio --name=randread-iouring --ioengine=io_uring --rw=randread --bs=4k --iodepth=32
--direct=1 --size=4G --numjobs=1 --runtime=30 --group_reporting
结果对比表:
| 指标 | libaio | io_uring | 差异方向 |
|---|---|---|---|
| 随机读IOPS | 约100K | 约115K | io_uring高15% |
| 平均延迟(clat) | 280us | 240us | io_uring低40us |
| P99延迟 | 890us | 780us | io_uring低12% |
| CPU占用率 | 较高(系统调用频繁) |
略低(轮询模式分摊) | io_uring更优 |
统计结果显示,io_uring在随机读和高队列深度场景下优势最明显,IOPS提升约10%-15%,P99延迟降低约10%,但在顺序读/顺序写场景下,两者的吞吐差距很小,通常只有1%-3%,可能属于测试噪声范围。
如果虚拟机内核版本低于5.1(比如CentOS 7自带的3.10内核),io_uring根本不可用,那就只能选libaio,这是选型时的硬性门槛,务必先确认版本。
根据业务场景选择ioengine的策略
不同业务负载落在虚拟机里,对ioengine的要求侧重点不同,成套的选择方案如下。
数据库服务器磁盘测试:选io_uring或libaio
数据库(比如MySQL、PostgreSQL)跑在虚拟机上时,通常会有大量小随机读请求,且对延迟极其敏感,此时建议优先用io_uring做fio压测,模拟真实业务负载,若数据库内核版本过低无法用io_uring,改用libaio并把iodepth调低到8-16,更贴近OLTP行为的I/O深度特征。
文件服务器或NFS存储:选libaio
并发访问量大但单次I/O体积较大(如视频、备份文件)的场景,libaio足够胜任,大块顺序读写下,io_uring的轮询优势会被吞吐瓶颈掩盖,多此一举增加复杂度,直接保持libaio模式即可满足测试需求。
云服务器fio压测定位故障:交错验证
当虚拟机里的存储性能疑似异常(比如IOPS远低于购买规格)时,建议先用sync做一个基准跑分,再用libaio复测,最后加一组io_uring测试,三者对比能快速定位问题所在:
- 若sync与libaio差距小,说明虚拟化层I/O路径存在严重排队,宿主机可能超卖严重。
- 若libaio正常但io_uring异常,说明新内核特性与虚拟化驱动兼容性有隐患。
- 若三者均不达标,优先检查磁盘类型和购买规格是否匹配。
Windows虚拟机场景:固定选libaio
Hyper-V或VMware的Windows虚拟机里没有io_uring可用,libaio也是凤毛麟角(除非用WSL2),Windows平台只有Windows原生AIO或直接使用同步I/O,Fio官方文档也建议Windows用户使用libaio之外的windowsaio引擎,这个需要额外注意。
测试磁盘性能的通用操作路径
无论选哪个ioengine,实测时都要先确认内核版本、确认块设备是否支持direct I/O、是否开启了virtio多队列,三个检查一步都不能少,具体操作路径如下:
# 查看内核版本 uname -r # 检查块设备是否支持direct I/O(输出非空即可) blockdev --getdirectio /dev/vda # 检查virtio多队列是否开启 ls /sys/class/block/vda/mq/
虚拟机里fio测性能方案要想结果可信,还建议给虚拟机分配至少2个vCPU专门处理中断,并注意宿主机上的其他虚拟机是否在跑重负载,避免I/O噪声干扰测试。
ioengine选择影响最大的是延迟而非吞吐
多数用户在选engine时只盯着IOPS和带宽数字,但真正受engine影响的其实是延迟分布,特别是io_uring在polling模式下能有效规避virtio中断路径上的调度延迟,让延迟长尾大幅缩短,对于延迟敏感型业务(如Redis、交易系统),这个特性非常关键。
如果测试结果只关心顺序带宽,那engine的影响确实很小,几乎可以忽略不记,但一旦涉及随机读写和延迟极值,选对engine带来的收益是实打实的。
关于结尾:虚拟机下fio ioengine选哪个这个问题其实没有非黑即白的答案,只要是Linux 5.1以上内核,优先尝试io_uring,顺手做组对比;凡是内核较老、环境不可控或追求稳定复现的场景,libaio依然是不可撼动的标准选择。
Q&A
fio ioengine选libaio好还是io_uring好?
没有绝对优劣,取决于虚拟机内核版本和测试目标,内核对io_uring的支持从5.1才正式引入,且初版功能不完整,做压测追求低延迟高IOPS首选io_uring;做基准对比、与历史数据对照则坚持libaio不变,保证测试口径一致。
虚拟机里fio块大小设置多少最合适?
取决于模拟的业务类型,随机小I/O场景(如OLTP数据库)一般用4k或8k;视频流媒体等大块读写场景用64k到128k;混合负载建议多跑几组bs值对比,块大小与ioengine的交互关系在于,小bs对异步引擎的提交效率要求更高,iouring在此环境下优势更大。
公有云虚拟机上fio压测结果远低于物理机规格,是ioengine设置错了吗?
不完全是,公有云虚拟机磁盘性能受宿主CPU调度、邻居虚拟机争抢、存储后端网络延迟等多重因素影响,ioengine选错只会放大性能问题,而非根源,先用fio --ioengine=sync做小队列深度测试,结果若与libaio相当,说明虚拟化层I/O路径有瓶颈,此时无论换哪个engine结果类似。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/626136.html





