物理主机 NVMe 性能如何验收:从文件级测试到延迟判读

物理主机 NVMe 性能如何验收:从文件级测试到延迟判读

新机器准备承载数据库或文件服务时,复制一个大文件得到的速度不足以验收存储。文件可能命中缓存,写入可能尚未持久化,后台任务也可能改变结果。更可靠的做法是固定测试文件、块大小与并发条件,分别观察吞吐和延迟,再保留可以复测的原始记录。

CloudUDP 的物理主机标配两块 1TB NVMe,各自直连,没有硬件 RAID。以下以 Debian 13 为例,用 fio(可配置的 I/O 负载测试工具)测试指定文件系统,不对裸设备写入,也不把测试结果当作业务性能承诺。

一、先确认测试边界与实际落盘位置

文件级测试覆盖的是“文件系统到存储”的路径,并非单独测量盘的极限。加密、卷管理、镜像以及挂载选项都可能影响结果。两块盘各自直连,也不意味着某个目录会同时使用两块盘。

先安装工具并检查目标目录。以下把 /srv 作为待确认的测试位置;若它不在目标文件系统上,应换成实际挂载目录。

sudo apt-get install fio
findmnt -T /srv -o TARGET,SOURCE,FSTYPE,OPTIONS
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
df -h /srv
df -i /srv
fio --version

核对可用空间、挂载状态与目录权限。不要仅看盘的标称容量;文件系统开销和已有数据会占用空间。测试文件写满后还必须留出业务增长余量。

不要把 /dev/nvme… 这样的设备路径传给写测试。独立文件减少误覆盖风险,但不能消除资源竞争;优先在业务上线前执行,出现业务延迟异常、空间逼近预留线或 I/O 错误时立即停止。

二、建立独立目录并准备完整测试文件

所有命令在同一个 Bash 会话中执行。由管理员创建只属于当前操作账户的临时目录,测试和结果都放在其中,避免碰到现有业务文件。

TESTDIR=$(sudo mktemp -d /srv/fio-acceptance.XXXXXX)
sudo chown "$(id -u):$(id -g)" "$TESTDIR"
chmod 700 "$TESTDIR"
export TESTDIR
printf '%s\n' "$TESTDIR"
findmnt -T "$TESTDIR"

示例使用 1GiB 文件,是流程验证的起点,不代表整个存储容量或长期稳态表现。size=1G 在 fio 中按二进制单位解释,不能与设备标称容量混算。确认空间充足后,先顺序写满文件:

fio --name=prepare \
  --filename="$TESTDIR/test.bin" \
  --size=1G --rw=write --bs=1M \
  --ioengine=libaio --direct=1 \
  --iodepth=1 --numjobs=1 --end_fsync=1 \
  --group_reporting --output-format=json \
  --output="$TESTDIR/prepare.json"

必须检查退出状态和 JSON 中的错误字段。若直接 I/O 不受支持,应先核对文件系统与挂载方式,不要悄悄改成缓存 I/O 后继续比较。准备阶段有错误时,后续读测试没有可信基础。

direct=1 通常绕过文件数据的页缓存,但不绕过全部设备缓存。end_fsync=1 在任务结束时请求同步,不表示每次写入都模拟数据库提交。

三、固定参数,分别测试随机读与随机写

先做单任务随机读,再做随机写,避免混合负载掩盖问题。测试期间不要同时复制数据或运行其他存储测试。

fio --name=randread \
  --filename="$TESTDIR/test.bin" \
  --size=1G --rw=randread --bs=4k \
  --ioengine=libaio --direct=1 \
  --iodepth=8 --numjobs=1 \
  --time_based --runtime=30 --ramp_time=5 \
  --group_reporting --output-format=json \
  --output="$TESTDIR/randread-q8.json"
fio --name=randwrite \
  --filename="$TESTDIR/test.bin" \
  --size=1G --rw=randwrite --bs=4k \
  --ioengine=libaio --direct=1 \
  --iodepth=8 --numjobs=1 \
  --time_based --runtime=30 --ramp_time=5 \
  --end_fsync=1 --group_reporting \
  --output-format=json \
  --output="$TESTDIR/randwrite-q8.json"

这里的运行时间和预热时间仅是测试参数。短测试可能受到设备缓存、温度与后台整理影响;不能据此推导长时间写入性能。

补做低队列深度对照

将读测试的 iodepth 改为 1,输出文件改为 randread-q1.json,其余参数保持不变。队列深度表示允许同时未完成的 I/O 请求数,不等于 CPU 核心数,也不是业务并发连接数。

若提高队列深度后吞吐增加、尾延迟明显变差,只能说明该负载下存在吞吐与等待时间的权衡。不要直接照搬到数据库连接池。

四、把吞吐、IOPS 与尾延迟一起读

IOPS 是每秒完成的 I/O 次数。块大小固定时,它与吞吐存在对应关系;不同块大小的结果不能只按 IOPS 排名。fio JSON 中 bw 通常以 KiB/s 表示,读取时应核对版本和字段单位。

重点查看 read 或 write 内的 iops、带宽字段,以及 clat_ns 的平均值和百分位。完成延迟不完全等于应用端响应时间,应用排队与处理还在这条测量链之外。

P99 表示约九成九请求的延迟不超过该值,不是最大延迟。平均值相近而 P99 上升,可能意味着少量请求出现较长等待;必须结合实际队列深度和重复测试判断。

验收记录至少包括:

还要查看 fio 的实际 I/O 深度分布。配置为 8 不代表运行中一直达到该深度;若引擎或文件系统行为不同,名义参数相同也未必可比。

五、复测、归档与清理形成验收闭环

每种负载重复执行,使用不同输出文件名,不覆盖前次结果。比较波动范围,而不是挑最高数字。扩大测试文件或延长时间前,应重新检查空间,并明确新增写入量与资源竞争。

两块盘需要分别验收时,应选择各自文件系统中的目录,再用相同参数串行测试。如果目录实际落在同一个卷上,换目录不能算第二块盘的独立验收。

若读写测试出现错误,先保留原始输出和内核相关记录,再排查文件系统与设备链路。若没有错误但波动较大,先排除同机任务和测试条件变化,不要立即认定硬件异常。

完成后把 JSON、挂载信息与命令记录复制到受控归档位置。确认归档可读,再核对测试目录并仅删除本次生成的文件:

printf '%s\n' "$TESTDIR"
ls -lh -- "$TESTDIR"
rm -- "$TESTDIR/test.bin"

剩余结果文件确认已归档后逐个清理,目录为空再用 rmdir 删除,不使用递归删除设备挂载点。最终结论应写成“在这些参数与负载条件下得到这些结果”,而不是“这块盘始终达到某个速度”。文件级基线负责发现明显偏差,应用上线前仍需以真实读写模式验收。

常见问题

fio 的文件级测试是否完全不会影响生产业务?

不是。独立文件可以避免直接覆盖已有文件,但测试仍会竞争磁盘、CPU 与文件系统资源。应优先在业务上线前执行;已有业务时,需要先评估资源竞争并设定停止条件。

direct=1 是否等于绕过全部缓存并模拟数据库提交?

不是。direct=1 通常通过直接 I/O 绕过文件数据的页缓存,但不会绕过全部设备缓存,也不等同于数据库的持久化提交。事务落盘行为需要结合应用及同步写语义单独验收。

EPYC 7543P 独享物理主机,$454/月起

物理主机按月整机出租

配置项与本期应付在结账前核对,所有价格以美元结算。