香港服务器运行Rocky Linux 9.5,NVMe SSD的IOPS提升如何验证?
香港服务器上的 NVMe SSD 是否真的有 IOPS 提升,不能只看产品名称、/dev/nvme0n1 设备名或一次 fio 峰值。更可靠的验证方式,是在 Rocky Linux 9.5 环境中固定测试文件、块大小、并发度和运行时间,先建立基线,再一次只改变一个变量,连续采样 IOPS、吞吐、平均延迟和 P99 延迟。
如果比较的是两种存储方案,也不能在更换硬盘的同时改变 vCPU、内存、文件系统、内核或测试时段。下面的流程以香港服务器本机测试为主,示例数据仅用于演示如何判断,不代表某一台A5IDC服务器的实际测量结果。
先明确“IOPS提升”要验证什么
IOPS、吞吐与延迟不是同一个指标
IOPS表示每秒完成的I/O操作数量。对于小块随机读写,IOPS通常是主要指标;对于备份、视频、镜像分发等连续读写任务,吞吐量更有参考价值。
常用换算关系如下:
- IOPS = 每秒完成的I/O操作数量
- 十进制MB/s = IOPS × 每次I/O字节数 ÷ 1,000,000
- 二进制MiB/s = IOPS × 每次I/O字节数 ÷ 1,048,576
- 4KiB I/O中的4KiB通常按4096字节计算
例如,4KiB随机读达到398,000 IOPS时:
398,000 × 4,096 ÷ 1,000,000 ≈ 1,630.2 MB/s
换算为二进制单位约为:
398,000 × 4,096 ÷ 1,048,576 ≈ 1,554.7 MiB/s
因此,不能把“398,000 IOPS”直接理解为“398,000 MB/s”。两者的块大小和单位完全不同。
延迟还需要单独观察:
| 指标 | 含义 | 适合回答的问题 |
|---|---|---|
| IOPS | 每秒完成的I/O数量 | 存储系统能处理多少小块请求 |
| BW或吞吐 | 每秒传输的数据量 | 连续读写或大块I/O速度如何 |
| 平均延迟 | 所有请求的平均完成时间 | 常态响应速度如何 |
| P95 | 95%的请求延迟不高于该值 | 大多数请求是否稳定 |
| P99 | 99%的请求延迟不高于该值 | 尾延迟是否明显抖动 |
| CPU占用 | 测试期间处理I/O所消耗的CPU | 是否由CPU或并发线程限制 |
iostat中的await | 块设备层观察到的等待时间 | 内核块层是否出现排队 |
对于数据库、缓存、消息队列等应用,只看IOPS容易误判。例如某个配置将IOPS从40万提升到48万,但P99延迟从220微秒升到900微秒,那么它可能更适合批量吞吐,不一定适合延迟敏感的在线事务。
“提升”必须有对照组
验证提升至少需要两个状态:
- A状态:基线配置,例如当前香港服务器的默认磁盘和系统环境。
- B状态:只改变一个变量,例如更换存储方案、调整队列深度或改变文件系统。
- A、B状态使用相同的测试文件大小、块大小、读写比例、运行时长和采样次数。
相对提升率可以按下面的方式计算:
提升率 =(B状态中位数 - A状态中位数)÷ A状态中位数 × 100%
例如,A状态中位数为398,000 IOPS,B状态中位数为476,000 IOPS:
(476,000 - 398,000)÷ 398,000 × 100% ≈ 19.6%
这个结果只能说明在指定测试条件下,B状态的IOPS中位数高约19.6%。它不能直接推导出所有应用都能提升19.6%,也不能说明连续读写、同步写入或远程访问一定有相同比例的变化。
在Rocky Linux 9.5上建立基线
记录系统、内核和存储设备状态
Rocky Linux 9.5属于RHEL兼容环境,但实际运行的内核、微码、NVMe驱动、文件系统和软件包状态仍然需要单独记录。相同的Rocky Linux小版本,如果安装过不同时间的更新,实际内核也可能不同。
可以先执行以下命令保存环境信息:
cat /etc/rocky-release
cat /etc/os-release
uname -r
rpm -q kernel fio nvme-cli sysstat libaio
lsblk -e7 -o NAME,MODEL,SERIAL,SIZE,ROTA,TYPE,FSTYPE,MOUNTPOINTS
findmnt -T /data
df -hT /data
fio --version
fio --enghelp | grep -E 'libaio|io_uring'
其中/data替换为实际测试盘的挂载路径。需要重点记录:
- Rocky Linux发行版版本和具体内核版本;
- vCPU数量、内存容量和实例规格;
- NVMe设备名称、容量、型号和命名空间;
- 文件系统类型,例如XFS或ext4;
- 挂载参数,例如是否使用
noatime; fio版本和可用的I/O引擎;- 测试盘是本地盘、虚拟NVMe设备还是云平台块存储。
/dev/nvme0n1只说明系统把设备识别为NVMe命名空间,并不能单凭设备名证明它一定是物理直通盘。虚拟化平台可能将共享后端存储以NVMe形式呈现,最终性能还可能受到宿主机、存储策略或套餐IOPS上限影响。
如果系统提供nvme-cli,可以补充查看控制器信息:
sudo nvme list
sudo nvme list-subsys
sudo nvme id-ctrl /dev/nvme0
sudo nvme smart-log /dev/nvme0
虚拟设备有时不开放完整的控制器信息,smart-log执行失败不一定代表磁盘性能异常。此时应以平台提供的设备类型、IOPS上限和吞吐限制为准,并在测试记录中注明无法读取硬件健康信息。
安装测试工具时不要改变测试环境
如果系统缺少测试工具,可以在正式采样前一次性安装:
sudo dnf install -y fio nvme-cli sysstat
安装软件本身不会改变磁盘性能结论,但不要在A、B两轮测试之间执行dnf update、更换内核或升级fio。如果确实需要更新,应在更新并重启后重新建立基线。
测试期间还应记录是否有以下后台任务:
systemctl list-timers --all
ps -eo pid,comm,%cpu,%mem,stat --sort=-%cpu | head -n 20
备份、日志压缩、数据库维护、镜像同步和自动更新都可能造成存储抖动。不要为了测试而盲目停止生产服务、关闭安全策略或修改防火墙;如果服务器承载业务,应优先使用独立测试盘或业务低峰期。
使用测试文件而不是生产数据盘
文件级测试更接近应用实际访问方式,也比直接对原始块设备进行测试更安全。先创建专用目录:
sudo mkdir -p /data/fio-test
sudo chown "$USER":"$USER" /data/fio-test
/data/fio-test/benchfile必须是专门用于测试的文件。后续随机写入会覆盖该文件内容,因此应确认目录中没有业务文件,并保证测试盘有足够空间。
首次建立测试文件时,可以使用一次顺序写入预分配测试区域:
fio --name=prepare \
--filename=/data/fio-test/benchfile \
--rw=write \
--bs=1M \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=16 \
--numjobs=1 \
--end_fsync=1 \
--group_reporting=1
这里的32G只是演示值。实际容量不足时可以调整,但测试文件不能小到完全落在内存缓存或只覆盖极小的存储范围。准备阶段不计入性能结果,后续测试只读取或改写这个专用文件。
不要直接把fio的--filename指向正在使用的/dev/nvme0n1。原始设备测试可能覆盖分区表、文件系统和业务数据。如果必须进行原始设备测试,应使用独立空盘,先完成备份和恢复演练,并由操作者确认设备路径;测试结束后的恢复通常需要重新创建文件系统或从备份恢复数据。
用固定参数完成第一轮基线
选择一个可复现的4KiB随机读配置
下面是一组适合观察小块随机读能力的基线参数:
fio --name=baseline-randread \
--filename=/data/fio-test/benchfile \
--rw=randread \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=8 \
--numjobs=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=normal
这组参数的含义是:
--rw=randread:执行随机读,不混入写入;--bs=4k:每次I/O为4KiB;--direct=1:尽量绕过操作系统页缓存,减少内存缓存的干扰;--iodepth=8:单个作业最多保持8个异步I/O请求;--numjobs=1:只使用一个fio作业,便于先观察单线程队列深度;--ramp_time=20:前20秒作为预热,不计入正式统计;--runtime=60:预热后正式采样60秒;--lat_percentiles:输出延迟分位数;--randrepeat=1和--norandommap=1:让随机访问模式更容易重复。
libaio是否可用应以fio --enghelp的结果为准。如果系统没有该引擎,可以记录后改用可用的异步引擎,但A、B两组必须使用同一个引擎。不能一组使用libaio,另一组使用io_uring,然后把差异全部归因于NVMe存储。
建议先执行一次预热,再连续完成5次正式采样。可以使用以下方式保存JSON结果:
for n in 1 2 3 4 5; do
fio --name=baseline-randread \
--filename=/data/fio-test/benchfile \
--rw=randread \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=8 \
--numjobs=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=json \
--output="baseline-${n}.json"
sleep 10
done
每次运行都要保留原始结果,不要只记录最高的一次。5次结果可以计算中位数、最小值、最大值和变异范围。若其中一次受到备份任务、CPU抢占或系统异常影响,应在记录中保留,而不是直接删除;可以另外标注该次采样不适合用于主判断。
同时采集CPU和块设备层数据
fio反映的是应用测试进程看到的结果,iostat和mpstat可以帮助判断瓶颈位于存储、CPU还是虚拟化层。可以在另一个终端执行:
iostat -xz 1 85 > iostat-baseline.log
mpstat -P ALL 1 85 > mpstat-baseline.log
vmstat 1 85 > vmstat-baseline.log
需要关注以下现象:
- CPU接近满载,IOPS随着并发增加却不再上升,可能是测试线程或协议栈达到瓶颈;
- 虚拟机的
vmstat中st明显升高,可能存在宿主机CPU争用; iostat中的队列长度持续增加,但IOPS增长很小,说明设备或后端存储可能已经饱和;fio的P99明显升高,而平均延迟变化不大,说明少量请求出现长尾;- NVMe温度随连续写入持续升高,后续轮次性能下降时要考虑热降速。
fio中的完成延迟和iostat中的await不处于完全相同的观测层,不能直接当作同一个数值比较。前者更接近fio请求的完成时间,后者包含块设备层排队和处理时间。
每一轮只改变一个关键变量
变量一:队列深度
队列深度是最容易造成“IOPS提升”错觉的因素之一。很多NVMe设备在低队列深度下并未达到自身吞吐能力,增加队列后IOPS会明显上升,但延迟也可能同步增加。
保持基线中的读写类型、块大小、文件、运行时间和作业数量不变,只改变iodepth:
for qd in 1 4 8 16 32; do
fio --name="qd-${qd}" \
--filename=/data/fio-test/benchfile \
--rw=randread \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth="$qd" \
--numjobs=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=json \
--output="qd-${qd}.json"
done
以下数据是用于解释判断方式的模拟示例:
| 每作业队列深度 | IOPS | 平均完成延迟 | P99延迟 | CPU占用 |
|---|---|---|---|---|
| 1 | 118,000 | 62微秒 | 96微秒 | 8% |
| 4 | 302,000 | 89微秒 | 152微秒 | 18% |
| 8 | 398,000 | 117微秒 | 212微秒 | 34% |
| 16 | 472,000 | 176微秒 | 390微秒 | 57% |
| 32 | 480,000 | 310微秒 | 880微秒 | 86% |
这个示例中,队列深度从8提升到16后,IOPS增加约18.6%,但P99延迟也明显升高;从16增加到32时,IOPS只增加约1.7%,P99却继续上升。更合理的解释是存储已经接近饱和,而不是“队列越大越好”。

如果实际应用只有队列深度1至4,那么应重点参考低队列结果。拿队列深度32的峰值与业务队列深度1的结果比较,会夸大NVMe对真实应用的帮助。
变量二:读写类型和读写比例
随机读、随机写以及混合读写属于不同工作负载,不能用一个结果替代另一个结果。完成随机读基线后,可以分别进行随机写和混合读写测试。
随机写测试会改变测试文件内容,因此必须确认使用的是专用测试文件:
fio --name=randwrite-4k \
--filename=/data/fio-test/benchfile \
--rw=randwrite \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=8 \
--numjobs=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=json \
--output=randwrite-4k.json
混合读写则使用randrw和rwmixread指定读比例:
fio --name=randrw-70read \
--filename=/data/fio-test/benchfile \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=8 \
--numjobs=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=json \
--output=randrw-70read.json
不要把随机写的IOPS和随机读的IOPS直接相加,也不要用70%读、30%写的结果代表纯读业务。数据库、日志和缓存系统的同步策略不同,写入结果尤其容易受到设备缓存、文件系统和fsync行为影响。
变量三:同步写入与普通直接写入
--direct=1只说明测试绕过了页缓存,不等于每次写入都已经持久化到介质。对于需要确认落盘时间的应用,还应单独测试同步写入。
例如,可以使用单队列、4KiB写入和每次写入同步的配置:
fio --name=sync-write-4k \
--filename=/data/fio-test/benchfile \
--rw=randwrite \
--bs=4k \
--size=32G \
--ioengine=libaio \
--direct=1 \
--iodepth=1 \
--numjobs=1 \
--fsync=1 \
--runtime=60 \
--ramp_time=20 \
--time_based=1 \
--group_reporting=1 \
--randrepeat=1 \
--norandommap=1 \
--lat_percentiles=1 \
--percentile_list=50:95:99:99.9 \
--output-format=json \
--output=sync-write-4k.json
这一轮不能和普通随机写的结果混在一起。同步写通常会显著降低IOPS并提高延迟,但它回答的是“应用等待持久化时的响应速度”,而不是“设备在高队列下能完成多少后台写请求”。
如果某个方案在普通随机写中有较高IOPS,但同步写P99很高,则它更适合对持久化确认不敏感的批量任务,不应直接宣传为所有数据库写入场景都更快。
变量四:块大小
块大小改变后,IOPS和吞吐的关系也会改变。4KiB、8KiB、16KiB和1MiB测试的是不同能力:
- 4KiB或8KiB:更接近部分数据库页、缓存和索引访问;
- 16KiB或32KiB:可用于观察中等大小业务请求;
- 1MiB:更接近备份、镜像和大文件连续传输。
块大小测试时,队列深度、读写模式和运行时长必须保持不变。例如基线为4KiB随机读,则只把--bs=4k改为--bs=8k或--bs=16k,不要同时修改iodepth和numjobs。
当块大小从4KiB增加到1MiB时,IOPS可能下降,但吞吐量上升,这是正常现象。不能仅以IOPS下降判断NVMe性能变差,应同时查看BW,并用统一单位换算。
文件系统、内核和时间段的影响
文件系统差异要单独测试
XFS和ext4的文件系统行为可能不同,挂载参数也会影响元数据和同步写入表现。比较文件系统时,应准备两个独立的测试卷,或者使用可恢复的快照,确保业务数据已经完成备份。
不要在包含生产数据的分区上直接执行格式化命令。更换文件系统涉及数据覆盖、挂载点变化和服务启动依赖,适用前提是测试盘为空或已有可验证备份;回滚方式是卸载测试卷、恢复原文件系统或从备份恢复,不应把生产盘作为试验对象。
文件级测试的结果包含文件系统和虚拟文件层开销,更接近应用实际情况;原始设备测试更接近后端块设备上限,但风险更高,也可能脱离实际应用路径。两者不能混用来证明同一个结论。
内核或驱动变化必须重新建基线
如果A状态使用旧内核,B状态同时升级到新内核并更换NVMe方案,即使IOPS发生变化,也无法判断到底是驱动、调度器、内核补丁还是存储介质造成的。
更换内核时至少记录:
uname -r
rpm -q kernel
lsmod | grep nvme
完成内核更新后需要重启才能使用新内核。此时应重新执行完整基线,而不是拿重启前最后一次结果直接作为A状态。Rocky Linux的RHEL兼容性有助于保持工具链和系统管理方式一致,但并不意味着不同内核、不同存储控制器或不同云平台后端会得到相同的IOPS。
测试时间段也应作为独立变量
香港服务器的存储后端可能存在共享资源。白天业务高峰、夜间备份时段和低负载时段可能得到不同结果。时间段测试可以作为独立变量,但不能在同一轮中同时更换线路、实例、内核和盘型。
建议记录:
- 香港机房或可公开识别的区域信息;
- 实例规格和磁盘套餐;
- 测试开始和结束时间;
- 当时的CPU、内存、磁盘和网络负载;
- 是否存在备份、同步或发布任务;
- 测试期间的温度和系统日志异常。
如果不同时间段结果差异很大,应先说明共享资源或后台任务的影响,不能直接把高峰期的低IOPS归因于Rocky Linux 9.5。
香港服务器场景要把线路因素剥离
在服务器本机执行的fio测试,主要衡量服务器本地存储路径,不包含用户到香港机房之间的网络延迟。更换访问地区、网络运营商或线路后,远程页面响应可能改变,但这不等于NVMe IOPS发生变化。
可以把验证拆成两层:
- 本机存储层:在香港服务器内部执行相同的
fio配置,观察IOPS、BW和延迟。 - 应用访问层:通过相同的HTTP接口或业务程序访问测试文件,另外记录客户端到服务器的往返延迟、首字节时间和下载速度。
如果远程访问变慢,而本机fio结果不变,优先检查网络路径、应用进程、连接数和服务器负载。如果本机IOPS下降,同时iostat队列、CPU争用或宿主机等待增加,再考虑存储后端或实例资源问题。

因此,香港地区是部署和网络体验的条件,不应被直接当成NVMe性能变量。比较不同香港服务器产品时,最好固定机房区域、实例规格和磁盘容量;如果这些条件无法固定,就只能把结果表述为两台完整服务器的综合差异,不能声称差异全部来自NVMe SSD。
结果如何形成可复核的判断
用中位数而不是单次峰值
每个条件建议至少完成1次预热和5次正式运行。记录每次的IOPS、BW、平均延迟、P95、P99、CPU占用和异常说明。
一个用于演示的汇总表如下:
| 条件 | IOPS中位数 | 平均完成延迟 | P99延迟 | 5次结果波动 |
|---|---|---|---|---|
| A:基线存储,4KiB随机读 | 398,000 | 117微秒 | 212微秒 | 约2.4% |
| B:仅改变存储方案,4KiB随机读 | 476,000 | 132微秒 | 410微秒 | 约3.1% |
按IOPS中位数计算,B比A高约19.6%;但P99延迟约增加93.4%。因此更准确的表述应是:“在该4KiB随机读和队列深度条件下,B的吞吐能力更高,但尾延迟也更高。”不能简单写成“B全面提升19.6%”。
如果多次运行的波动已经接近两种方案的差异,例如A和B只相差3%,而单组测试本身波动达到4%至5%,则应增加采样次数、延长观察时间或检查后台负载,不能把小幅差距当作稳定优势。具体的可接受波动阈值取决于业务要求,±3%至±5%只能作为测试阶段的参考范围,不是通用标准。
观察是否出现饱和点
当队列深度逐步增加时,可以绘制“队列深度—IOPS”和“队列深度—P99延迟”两组曲线:
- IOPS持续上升、P99变化较小:说明还有并发处理空间;
- IOPS开始趋平、P99快速上升:说明继续增加并发主要是在排队;
- IOPS和CPU占用同时达到高位:可能是实例计算能力限制;
- IOPS不高但
await和P99明显上升:可能存在后端存储争用或虚拟化抖动; - 读性能稳定、写性能在长时间运行后下降:需要检查写放大、缓存耗尽、温度或平台写入策略。
这类曲线比单个“最大IOPS”更能说明服务器适合什么负载。数据库在线事务通常更关注低队列下的P95/P99;批量任务则可以接受较高队列和更高平均吞吐。
按业务类型选择测试组合
| 应用类型 | 优先测试 | 重点观察 |
|---|---|---|
| 数据库随机读 | 4KiB或8KiB随机读,队列深度1至16 | 低队列IOPS、P95、P99 |
| 数据库持久化写 | 4KiB随机写,fsync=1 | 同步写延迟和长尾 |
| 缓存或搜索索引 | 混合随机读写 | 读写比例变化后的尾延迟 |
| 日志追加 | 顺序写、同步写和持续运行 | 吞吐、落盘延迟、长时间稳定性 |
| 备份和镜像 | 1MiB或更大块的顺序读写 | MB/s或MiB/s、持续吞吐 |
| 静态文件服务 | 本机顺序读加远程HTTP访问 | 磁盘吞吐、网络带宽、首字节时间 |
这张表的作用是避免用单一的4KiB随机读结果代表所有应用。如果产品宣传强调高IOPS,应至少补充低队列、混合读写和同步写场景;如果主要面向备份或文件分发,则应把连续吞吐放在更重要的位置。
比较产品时同步记录资源和成本
如果两种香港服务器的vCPU、内存、磁盘容量或月度费用不同,可以增加以下归一化指标:
- 单vCPU对应的稳定IOPS;
- 每GiB存储对应的稳定IOPS;
- 单位月度成本对应的稳定IOPS;
- 达到目标P99延迟时的可用IOPS。
单位成本IOPS可以写成:
单位成本IOPS = 在指定延迟上限内的稳定IOPS ÷ 月度总成本
月度总成本应包含实例、系统盘、数据盘、备份和可能的流量费用。由于报价和资源库存会变化,正式评测时应填入同一时间获取的实际报价,并注明计费周期。不能拿不同计费口径下的价格直接做排名。
复测条件与适用边界
当Rocky Linux 9.5更新内核、fio版本、文件系统挂载参数、实例规格或NVMe存储方案后,应重新建立基线。服务器迁移到另一台宿主机、出现明显宿主机争用、温度异常、后台备份介入或磁盘容量接近上限时,也不宜继续沿用旧结果。
最终可引用的性能结论应带上完整条件,例如:
在香港指定服务器、Rocky Linux 9.5、指定内核、指定文件系统、4KiB随机读、直接I/O、单作业队列深度8、运行60秒并完成5次采样的条件下,B状态的IOPS中位数高于A状态,但P99延迟同步增加。
这样的表达能够说明测试范围、比较对象和限制。它不把一次峰值扩展成所有应用场景,也不会把香港网络线路、RHEL兼容环境或NVMe设备名称误认为性能提升的唯一原因。只有在本机基线、变量控制、重复采样和业务负载映射都完成后,才能判断这次IOPS变化是否对实际应用有价值。



