ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NVMe顺序读写基线测试指南:用fio校准服务器存储性能

NVMe顺序读写基线测试指南:用fio校准服务器存储性能 1. 为什么Day8要先跑一遍顺序读写基线测试1.1 存储基线测试在服务器测试里到底是干什么的做服务器测试时间久了你会发现一个规律大部分人拿到一台新服务器最着急的就是赶紧跑个分、装个应用看看快不快。但真正有章法的测试流程第一步永远是做基线baseline。我在Day8这个节点安排NVMe顺序读写基线测试本质上不是追求“把盘跑到极限”而是先搞清楚这台服务器在默认配置下存储子系统能提供一个什么样的性能起点。基线数据的价值不是给你发朋友圈炫耀数字的它是后面所有测试的对照物。你之后调BIOS、换驱动、改内核参数、调队列深度到底有没有效果效果是正向还是负向必须拿基线数据来比。没有基线的性能测试就像没有刻度的尺子你只知道“好像变快了”但说不清快了多少更别说定位是哪一层带来的变化。顺序读写测试又在这套体系里排在最前面因为它的干扰因素最少。顺序大块IO比如128KiB、1MiB几乎不考验IO调度器的随机寻址能力也不考验NVMe控制器在多队列并发下的调度策略它更像是在测一条高速公路的极限车速——路况好、没有红绿灯测出来的是这条链路本身能跑多快。所以顺序读写基线是所有存储测试里最容易复现、最不容易被环境干扰弄出“玄学数据”的一项。1.2 NVMe设备为什么能跑到这么高的带宽写4.6GiB/s、读6.8GiB/s这个数字放到SATA SSD时代是不可想象的。SATA 3.0的理论极限只有6Gbps换算过来也就是750MB/s左右实际可用还得打八折。而NVMe走的是PCIe总线以最常见的PCIe 3.0 x4为例单向理论带宽是3.94GB/sPCIe 4.0 x4则是7.88GB/sPCIe 5.0 x4直接翻倍到15.76GB/s。Day8实测读6.8GiB/s基本就是跑在PCIe 4.0 x4通道的实用极限附近了写4.6GiB/s则更多受限于盘内NAND闪存颗粒的写入速度。除了通道带宽的差异NVMe协议本身也和AHCI拉开了代差。AHCI是给机械硬盘时代设计的它的队列深度只有1一次只能处理一个命令。而NVMe协议原生支持最多65535个队列每个队列又能挂65535个待处理命令。这意味着NVMe设备可以同时接收、处理大量的IO请求配合多核CPU的多队列机制能真正做到并发压满PCIe链路。这也是为什么测试NVMe设备时iodepth和numjobs如果设置得太保守根本压不出真实性能——协议栈的并行能力没有被激活。2. 测试环境准备与工具选型2.1 先确认设备状态别急着跑fio很多新手拿到一台带NVMe盘的服务器第一件事就是敲fio命令开跑。我建议你先花两分钟做三件事确认操作系统识别了哪些NVMe设备确认设备健康状态确认固件版本。这三项信息会和后面的性能结果一起记录进测试台账缺一不可。设备识别用nvme-cli工具命令很简单nvme list输出里会列出系统里所有NVMe命名空间包括设备节点比如/dev/nvme0n1、型号、序列号、固件版本、容量等。这里有一个容易忽略的点有些服务器主板上插了NVMe转接卡或者用了U.2盘系统里可能识别出多个NVMe设备但接口速度不同PCIe 3.0 x4和PCIe 4.0 x4的盘性能差异巨大一定要确认你测的是哪一块。然后看一眼健康状态和固件# 查看设备详细信息确认链路速率和宽度 nvme id-ctrl /dev/nvme0n1 # 查看SMART健康信息 nvme smart-log /dev/nvme0n1 # 查看PCIe链路状态 lspci -vv -s $(lspci | grep Non-Volatile memory controller | awk {print $1} | head -1) | grep -E LnkCap|LnkStalspci输出的LnkCap是设备支持的链路能力LnkSta是当前实际协商出来的链路状态。如果盘本身支持PCIe 4.0 x4但LnkSta显示的是8GT/s x2说明链路只协商到了一半带宽这种情况跑出来的性能一定是瘸腿的。优先去检查物理插槽位置和BIOS里的PCIe bifurcation配置。2.2 fio版本选择与安装fio是目前Linux生态里最主流的IO基准测试工具几乎所有的存储性能测试报告都基于它。Day8用到的是fio 3.39这个版本已经比较新了支持最新的IO引擎特性。Debian/Ubuntu系直接用apt就能装apt update apt install -y fio fio --versionCentOS/RHEL系用yum/dnfdnf install -y fio fio --version如果你用的发行版仓库里fio版本特别老比如3.1x且测试场景涉及新特性可以考虑源码编译安装。编译过程不复杂依赖libaio-devel和zlib-develyum install -y libaio-devel zlib-devel make gcc wget https://github.com/axboe/fio/archive/refs/tags/fio-3.39.tar.gz tar xf fio-3.39.tar.gz cd fio-fio-3.39 ./configure make -j$(nproc) make install这里要特别提醒一点fio版本之间可能存在输出格式的细微差异同一组测试如果换了一个大版本输出里的字段名和格式可能会有变化。做基线测试时记录fio版本号后面所有对比测试都用同一个版本这是保证数据可对比的基本要求。3. fio顺序读写测试参数的选择逻辑3.1 核心参数逐个拆解顺序读写基线测试的fio命令不算复杂难的是理解每个参数为什么这么设。下面这条命令是我在Day8实际使用的先贴出来然后逐个参数说明。写测试fio --nameseqwrite --filename/dev/nvme0n1 \ --rwwrite --bs1M --iodepth32 --numjobs1 \ --direct1 --sync0 --time_based1 --runtime60 \ --group_reporting --norandommap --size100% \ --ioenginelibaio --thread --output/tmp/seqwrite_result.json \ --output-formatjson读测试fio --nameseqread --filename/dev/nvme0n1 \ --rwread --bs1M --iodepth32 --numjobs1 \ --direct1 --sync0 --time_based1 --runtime60 \ --group_reporting --norandommap --size100% \ --ioenginelibaio --thread --output/tmp/seqread_result.json \ --output-formatjson--rwwrite / --rwread明确指定这是纯顺序写还是纯顺序读。顺序写会填满整个设备空间这里再说一次千万不要在存有数据的盘上执行write测试。基线测试建议挂在专用测试机上或者用一块专门用于测试的盘。如果只能用系统盘那就只跑readwrite用文件模式并且控制在文件系统内但这样做性能会受影响文件系统本身有开销基线数据的“纯度”就没了。最稳妥的做法是物理隔离测试盘就是测试盘不装系统不存数据。--bs1M块大小。顺序读写测的是带宽上限块越大单位时间内传输的数据量越多越能逼近链路极限。1MiB是NVMe顺序测试最常用的块大小如果你还想横向对比可以额外跑一组128K的但基线报告里用1M作为主数据即可。--iodepth32队列深度。这是fio向操作系统一次性下发的IO请求数量。NVMe设备原生支持高队列深度iodepth设太小比如1根本喂不饱设备设太大比如256也不一定更好反而可能因为CPU处理中断的能力不足导致延迟飙升。对PCIe 4.0的NVMe盘来说iodepth32搭配numjobs1是一个稳妥的起点后面可以通过调整numjobs来横向扩展并发。--numjobs1并发任务数。每个任务会启动一个或多个线程独立发IO。numjobs1意味着只有一个线程在发IO。有人会问那怎么压满整个设备答案是通过iodepth。queue深度32加上NVMe多队列特性单线程完全可以跑满读带宽。如果单线程跑不满再考虑numjobs2/4但要记住——这条命令测的是“单任务视角”的顺序带宽和跑多个并发任务的总带宽是两个概念基线测试要明确标注你的并发模型。--direct1绕过操作系统页缓存直接读写设备。顺序读如果不加这个参数数据一旦进了page cache第二次跑就走内存了速度比NVMe还要快好几倍测出来的完全不是盘的真实性能。正式基线测试必须开direct确保数据路径是应用→用户态缓冲→内核态IO→NVMe控制器→NAND不要有任何缓存层干扰。--time_based1 --runtime60固定跑60秒而不是固定数据量。这样做的好处是多次测试之间时间可控也方便观察设备在持续负载下的性能变化。长短结合的话短跑3分钟看极限长跑10分钟看稳态Day8先用60秒拿基线数据后面如果有散热或者垃圾回收相关的测试再加长。--norandommap这个参数一般配合顺序读写使用。fio默认会维护一张随机映射表用于随机IO场景避免重复读写同一块区域。顺序场景下没必要维护这张表加上它减少不必要的内存开销。--ioenginelibaio指定使用Linux原生异步IO引擎。libaio可以让fio在等待IO完成的同时继续下发新IO这是实现高队列深度、高并发吞吐的基础。对比一下如果你用sync引擎那iodepth参数基本就没意义了因为sync引擎是发一个等一个。补充说一句对裸设备测试libaio是最通用的选择如果你是跑io_uring引擎的机器也可以用io_uring但这个要和内核版本配套基线场景默认libaio即可。--group_reporting把所有任务的结果汇总显示而不是每个任务单独输出一行。配合numjobs2以上时尤其有用汇总数据看总带宽更直观。3.2 测试文件大小要不要设置--size100%表示测试范围覆盖整个设备容量。这里有一个底层逻辑顺序写会填满整块盘而NVMe SSD内部有垃圾回收机制当写满一遍之后再写第二遍就会触发回收搬移性能会明显波动。所以如果你连续跑多轮写测试第二轮开始测的已经不是“干净盘”的性能了而是“脏盘稳态”性能。这就是为什么很多存储团队会区分FOBFresh Out of Box性能和稳定态性能。Day8的基线是FOB性能是这块盘出厂后第一次完整写入时的表现。等测试结束后如果需要做稳定态测试可以先做一轮全盘写或者用fio的--prefill参数手动填一遍盘再进行后续测试。这块知识后面讲到稳态测试时会展开这里先埋个伏笔。4. Day8实测过程与结果解读4.1 实测数据记录我用的测试机配置是双路处理器内存256GB主板上有一块PCIe 4.0 x4接口的NVMe企业级固态盘容量3.84TB固件已更新到最新版。操作系统是CentOS 7.9内核版本3.10.0-1160fio版本3.39libaio引擎。测试前确认了链路状态LnkCap和LnkSta都是PCIe 4.0 x416GT/s说明链路没有缩水。设备健康状态百分百温度38度属于正常范围。连续跑了两轮写测试和两轮读测试数据如下测试项第一轮第二轮平均值顺序写带宽4.5GiB/s4.7GiB/s4.6GiB/s顺序读带宽6.7GiB/s6.9GiB/s6.8GiB/s两轮之间没有做额外处理读测试是直接重跑写测试由于第一轮已经写满了全盘第二轮的写入其实已经触发了部分垃圾回收但数据仍然稳定在4.6GiB/s附近说明这块盘的稳态写入性能保持得不错。4.2 fio输出里的关键字段解读fio跑完后会输出一大段结果不要只看最后那个带宽数字以下几项是判断测试有效性的关键。以写测试为例Jobs: 1 (f1): [W(1)][100.0%][w4517MiB/s][w4517 IOPS][eta 00m:00s]w4517MiB/s当前实时写带宽。fio在运行过程中会周期性刷新这一行如果看到带宽掉到0说明IO可能中断了要么是盘在垃圾回收要么是CPU被打满了要么是fio进程异常。跑测试的过程中建议盯一下这个实时输出。IOPS4517等于带宽除以块大小1MiB块下IOPS数值上就是MiB/s的数字这个自洽性可以快速验证。如果实际IOPS和带宽换算出来对不上说明有些IO没有完成或者块大小设置有问题。最终汇总报告里需要看这几项{ write: { bw_bytes: 4865392640, iops: 4640.5, clat: { mean: 6636, p50: 5422, p99: 10055, p99.9: 23201, max: 132741 } } }bw_bytes4865392640按照字节算的写带宽换算过来是4.53GiB/s。clat是完成延迟completion latency单位微秒。p50表示50%的IO延迟都在5.4毫秒以内p99是10毫秒这是很重要的延迟分布数据。对于企业级NVMe盘顺序写1MiB块p50在5毫秒左右是正常水平。如果p50到几十毫秒说明路径上有瓶颈。max延迟132毫秒这个尖刺大概率是测试过程中某个瞬间的系统调度抖动不代表普遍情况只要99%以上的IO延迟在合理范围这个max可以忽略不必过度解读。读测试的报告结构和写测试完全一致只是数值不同p50延迟会更低一些顺序读的延迟一般在100-200微秒级别因为读不需要等NAND写入完成只要从颗粒里读出来就行。4.3 为什么写速度明显低于读速度顺序写4.6GiB/s和顺序读6.8GiB/s之间差了将近50%这是很多人第一次跑NVMe测试时都会有的疑问。原因在于NAND闪存的根本物理特性读操作可以直接从浮栅晶体管里感知电荷状态速度快且稳定写操作则需要先擦除旧数据block级别的擦除再把电荷注入电荷捕获层这个物理过程的耗时比读高一个数量级。再加上现代NVMe盘内部还有写缓存策略、垃圾回收、磨损均衡这些机制写路径要经过的逻辑步骤更多整体带宽自然上不去。你看到的4.6GiB/s已经是盘内多通道并行比如8通道或16通道NAND交错写入叠加出来的结果。如果这块盘用的是TLC或QLC颗粒写入速度还会因为SLC缓存耗尽而出现一个明显的跳水——第一天测基线的时候你看到的是缓存充裕时的速度持续写入一段时间后降下来的速度才是稳定态这点一定要区分清楚。所以读快写慢不是故障是NAND物理规律导致的正常现象。做趋势对比时不要拿读基线去要求写基线它们有不同的天花板。5. 测试中的坑为什么数据跑不满、怎么排查5.1 跑不到标称带宽的常见原因NVMe盘标称顺序读7000MB/s、写5000MB/s但你实测只有5GiB/s和3.5GiB/s这种情况太常见了。遇到性能不达标挨个排查下面这几个方向大多数问题都能找到答案。链路速率缩水是第一个检查点。PCIe接口协商可能跑在降速状态比如盘支持PCIe 4.0 x4只协商到了PCIe 3.0 x4理论带宽直接从7.88GB/s腰斩到3.94GB/s。这种情况常见于低端服务器主板PCIe槽位共享带宽或者盘插在了一个物理x8但实际只连通x4的槽位上。前面提到过的lspci命令就能查这是性价比最高的排查手段。CPU对NVMe性能的影响经常被忽视。先看中断亲和性NVMe多队列机制依赖MSI-X中断如果中断都打在一个CPU核心上这个核心就成了瓶颈。用mpstat观察跑测试时哪个核的%soft飙到100%再用cat /proc/interrupts配合/sys/block/nvme0n1/device/irq查看中断号然后用irqbalance或者手动设置smp_affinity把中断分散到多个核上。固件版本和驱动参数也会限制性能。有些企业级盘出厂固件比较保守更新日志里会写明“提升顺序写性能”之类的条目。内核NVMe驱动nvme内核模块在较老的内核上可能不支持某些高队列深度特性常见做法是升级内核或者调整nvme模块参数比如poll_queues、io_queue_depth但这些属于调优范畴基线测试建议先保持在系统默认状态下记录数据调优后的数据单独对比。温度导致的限速也别漏掉。NVMe盘在高负载下发热量大很多盘固件设了温度阈值比如85度超过之后会主动降速保护颗粒。跑测试时用nvme smart-log看temperature如果发现温度一路升到80度以上就得检查机箱风道和散热片贴合情况。服务器机箱里NVMe盘如果贴着显卡或者其他发热大户热量互相叠加降速几乎是必然的。5.2 为什么会跑出“先快后慢”的曲线如果你是拿fio的实时输出在那里盯着看可能会发现一个现象顺序写前10秒带宽特别高然后突然掉一截。这大概率是SLC缓存策略在起作用。现代TLC/QLC盘内部会划出一部分区域模拟SLC模式单层单元写入快这部分缓存写满之后盘得把数据从SLC区域搬到TLC区域边写边搬性能就会掉到TLC原生写入速度。跑基线测试时如果你的runtime设置只有30秒可能测的恰好是SLC缓存没有写满的“超水平发挥”数据如果设到10分钟那前几分钟和后面几分钟可能就是天壤之别。Day8我设置了60秒这个时长偏短刚好能覆盖到SLC缓存写完之前的完整阶段。在正式基线报告里我会建议至少把写测试的runtime拉到300秒以上并且记录分时段的带宽变化把“初始峰值”和“稳定吞吐”分开写。5.3 实测时最容易被忽略的4个细节测试盘一定不能是系统盘或者挂载了文件系统的盘。文件系统、日志、监控进程都会产生背景IO污染测试数据。裸设备测试用--filename/dev/nvme0n1指定但盘上千万别有正在写的业务数据。测试前检查是否有其他进程在IO等待队列里。用iostat -x 1看一下如果有明显不正常的await先用ps找出来再决定要不要清理。多盘阵列或者用了RAID卡直通模式的场景情况和单盘完全不一样RAID卡本身有缓存写测试必须考虑缓存策略而且RAID卡/背板扩容模式下测出来的数字不能代表单盘水平。Day8测的是主板直连的NVMe单盘其他场景后面单独开篇讲。测试结果要存档。别只截图命令行终端里的输出fio支持直接输出JSON格式的结果用--output-formatjson --output文件名.json把完整结果存下来。后面你如果要做性能对比、生成趋势报告JSON格式比人肉复制输出方便一百倍。6. 基线数据怎么沉淀成可用资产6.1 建一个标准化的测试记录模板跑完一次测试只是万里长征第一步把测试过程和数据记录下来才是能复现、能对比的关键。我这个百日计划从Day1开始就维护了一个测试台账每一篇都按统一的模板记录测试日期、操作系统内核版本、fio版本、设备型号和固件、插槽位置和链路状态、测试参数完整命令、结果摘要、测试过程中的异常现象。这些信息一个都不能少少了任何一个后面做问题回溯的时候就得重新测试。举个例子某次测试性能特别好你想查一下是哪个固件版本带来的提升结果台账里没记固件版本你可能就要一台台去重新刷固件验证耗时费力。台账模板里我还会留一栏“备注”写测试时的小插曲比如“第一轮测试中系统日志有NVMe error detected重启后恢复”这种信息很多时候比性能数字本身更有价值。6.2 基线数据的后续用法对比、调优、回归拿到4.6GiB/s写、6.8GiB/s读的基线之后后面至少有三种常见用法。第一种是硬件替换和升级后的性能回归。比如换了一块新固件的盘、加了一根转接线、更新了BIOS重跑同一套fio命令对比数据有没有异常变化。基线就是标准值任何偏离都能被快速发现。第二种是系统调优的量化验证。内核参数调整比如nvme.poll_queues、nr_requests、CPU调频策略、NUMA绑定、中断亲和设置每做一项改动就重跑一次同样的测试把数据记录在调优日志里。有基线在你能清楚知道每一项改动对性能的贡献是正的还是负的对成本较高的改动尤其有用。第三种是容量规划和应用性能预测。你的应用是数据库写日志为主、还是视频转码读文件为主顺序IO多还是随机IO多有了基线的带宽上限结合应用的实际IO模型可以粗略估算一台服务器能支撑多大的业务压力。比如你知道了这台服务器的顺序写上限是4.6GiB/s如果你的日志系统峰值写入需求是3GiB/s那就还有余量如果到了5GiB/s就说明需考虑拆库、加盘或者换更高性能的硬件。6.3 从单点测试走向体系化的性能视野Day8只是百日学习计划里存储测试的起步。顺序读写基线测完之后后续还有4K随机读写、不同队列深度下的性能曲线、混合读写比例测试、稳态性能测试、掉电保护测试、长时间老化测试等一大串。每项测试都有它自己的方法论和参数组合但所有测试都离不开一个共同的起点——你已经跑过全流程、攒下了一份可靠基线的环境。我的习惯是每测完一类项目就把这一天的测试命令、方法和心得整理成一页独立的cheat sheet方便后续复用。比如Day8的fio命令模板加上参数解释和执行注意事项就足以生成一个可以直接在下一台服务器上跑的测试包。测试不是一次性的它是可复用的基础设施你搭好一次后面会一直被调用。另外关于测试的自动化程度目前看起来一条命令一个文件似乎足够应付单机测试。但如果你管理的机器数量超过5台你会发现手动敲fio命令、手动解析JSON的方式效率太低。这时候可以考虑写一个简单的shell脚本或者用Ansible批量推送fio配置并收集结果。如果有几十台机器数据量更大了就要考虑结果存入数据库或者时序数据库配合Grafana画趋势图。Day8先不展开自动化但是你要有这个意识单点测试是方法论的验证批量执行才是工程效率的体现。7. 测试过程中的几条经验心得测试做到这里我想把几次踩坑得来的经验直接摆出来省得读者们再走一遍弯路。fio的--filename指定设备文件时一定要确认设备节点不会变。服务器重启后设备顺序可能变化今天/dev/nvme0n1是这块盘明天可能就变成另一块了。稳妥做法是先用nvme list确认当前设备名再执行fio命令不要盲目沿用历史测试脚本里的设备节点。测试前把BIOS里的电源管理策略设置为性能模式关闭C-states深度节能避免CPU在低负载时主动降频导致测试中CPU频率忽高忽低间接影响到IO提交和中断处理的效率。尤其是跑随机4K这类对CPU频率敏感的场景效果会很明显。但记住如果你要在“生产默认配置”下做基线这一步就不做——基线必须反映真实环境的默认状态调优项放在基线之后单独验证。写测试前记得确认盘上没有任何需要保留的分区。前面虽然提醒过但确实有同事在测试盘上忘了备份数据一条fio write命令下去所有分区表被冲掉了。测试数据宝贵测试盘要专用工具要熟练流程要谨慎这三点缺一不可。我测存储这么多年最大的感受是性能测试与其说是测硬件不如说是在测你对自己系统的理解程度。每一个参数背后都是对IO路径某个环节的建模每一个异常数字都需要用相应层级的知识去解释。这也是为什么我把学习计划命名为“百日”——花一百天把服务器每个子系统的原理、工具、指标和瓶颈排查方法都过一遍之后遇到任何性能问题都不会再觉得无从下手。
返回列表