H29

NVMe 与存储协议

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 NVMe 与存储协议要解决什么

把固态硬盘插到主板上,并不意味着 CPU 可以直接把文件名交给闪存。应用看到文件,文件系统把文件位置映射到逻辑块,操作系统的块设备层再把读写请求交给驱动。驱动需要用设备理解的命令,描述读哪里、写多少、数据放在哪段内存,以及怎样报告成功或失败。存储协议就是这套沟通规则。它连接软件与控制器,却不代替文件系统,也不规定应用怎样组织记录。

NVMe 是面向非易失性存储的主机控制器接口与命令体系。入门时可以把它理解为:主机与存储控制器约定怎样提交命令、访问数据缓冲区、返回完成结果。常见 NVMe SSD 经 PCIe 连接主机,控制器在设备内部管理闪存。主机通常访问逻辑块地址,不直接选择某颗 NAND 芯片上的物理页。闪存磨损管理、垃圾回收等内部工作会影响性能,但本课先研究主机可观察的协议边界。

为什么需要这样的接口?机械硬盘的寻道延迟较高,较少的并发请求就可能让设备忙起来;闪存设备可以同时处理更多操作,软件提交、锁竞争和通知开销就更容易成为限制。NVMe 提供适合并发的队列机制,使多个 CPU 核心能够向控制器提交工作。它降低了组织并发的接口障碍,却没有承诺每个负载都能达到最高带宽。请求太小、并发不足或 CPU 忙于别的工作,仍然会让设备吃不饱。

还要分清外形、链路与协议。M.2 是连接器和尺寸体系的一部分,不能仅凭外形断定使用 NVMe;某些 M.2 设备使用 SATA。2.5 英寸设备也可能通过合适的接口使用 NVMe。服务器中的 U.2、U.3、EDSFF 等形态涉及连接、散热与维护方式,实际支持什么仍要看背板、线缆和主板说明。采购清单若只写“高速 SSD”,就漏掉了兼容性所需的信息。

本课采用具体机器作为参照:1 台双路服务器,安装若干 CPU 核心、256 GiB 内存、1 块 PCIe 4.0 ×4 NVMe SSD,以及 1 张 25 Gb/s 网卡。这里列出的规格只是分析起点。SSD 所在插槽可能经过交换芯片,也可能与其他设备共享上行带宽;CPU 插槽与设备的位置还会影响内存访问路径。判断瓶颈时,需要把设备放回整台机器中,而不是只看硬盘标签。

从应用到存储介质的协议边界
从应用到存储介质的协议边界

2 队列、命令与完成路径

NVMe 的基本工作单位是命令。读命令要说明目标命名空间、起始逻辑块、长度和数据缓冲区的位置。命名空间可以先理解为控制器对主机提供的逻辑块地址空间;操作系统可能把它显示为独立的块设备。控制器、命名空间、分区、文件系统是不同层次。1 个控制器可以提供多个命名空间,1 个命名空间也可以再划分分区,不能看到多个设备名就认定机器上有同样数量的物理 SSD。

主机与控制器通过提交队列和完成队列交换信息。对常见的本地 PCIe NVMe,队列通常位于主机内存。驱动先准备数据缓冲区与命令,再把命令放入提交队列,更新门铃寄存器通知控制器有新工作。控制器读取命令,执行数据搬运与存储操作,随后向完成队列写入完成项。驱动观察完成项,识别是哪条命令结束,并根据状态决定向上层报告成功、错误还是进入恢复流程。

数据搬运通常依靠 DMA,也就是设备按授权访问主机内存。读操作把数据送入内存,写操作从内存取走数据;CPU 仍负责准备请求、处理映射与完成,但不需要逐字节替设备搬运所有内容。内存缓冲区在命令执行期间必须保持有效,驱动还要遵守内存可见性要求。这里知道“命令、数据、完成各有自己的路径”即可,地址映射和屏障的实现细节留到驱动课程。

完成项与中断也不是同一件事。完成项记录命令结果,中断是通知 CPU 来处理结果的方式之一。驱动还可以轮询完成队列,或者让多条完成共享较少的中断通知。没有收到某次预期中断,不一定代表设备完全没有完成工作;反过来,看到中断计数增长,也不能证明每条写入都成功。排查时要把设备状态、完成状态与通知机制分别观察。

队列深度是另一个容易混淆的词。设备允许的队列容量、测试工具设置的目标深度,以及运行时实际未完成请求数并不相同。工具设为 32,只表示尝试维持相应并发;如果上层串行等待、引擎不支持预期的异步提交,或请求被拆分合并,实际深度可能偏离设置。谈性能时,应说明统计位置与作用范围,例如“每个作业深度 8,共 4 个作业”,不能只说“深度 8”。

最后,命令完成的含义要结合命令类型、缓存配置与设备保证。普通写入成功,不应脱离上下文直接解释为掉电后必然保留。易失性写缓存、Flush、FUA 以及设备的掉电保护共同影响持久化语义。应用还可能通过文件系统同步接口表达要求。协议能传递这些要求,但操作者必须确认整个路径实际执行了它们,不能用一次普通写入的完成时间代替持久写延迟。

提交、数据与完成的路径
提交、数据与完成的路径

3 PCIe、NVMe-oF 的边界

PCIe 回答设备怎样通过本地串行总线与主机交换数据,NVMe 回答存储命令怎样组织。两者经常一起出现,但不能互换。PCIe 网卡也使用 PCIe,却不因此变成 NVMe 存储;同样,NVMe 命令还可以通过网络传输。看规格时,“PCIe 4.0 ×4”描述链路代际与通道数,“NVMe”描述存储接口与命令体系,它们分别约束不同问题。

以 PCIe 4.0 ×4 为例,每条通道每方向的信号传输率为 16 GT/s,采用 128b/130b 编码。忽略事务与协议开销,4 条通道每方向约能承载 7.88 GB/s。这里的 GB/s 使用十进制单位,不能直接当成 GiB/s。实际 SSD 顺序读取带宽还受控制器、闪存和负载影响。主板插槽写着 ×16,也可能只有 ×4 电气连接;设备最终协商到的速度和宽度才是分析当前链路的依据。

NVMe over Fabrics,简称 NVMe-oF,把 NVMe 的命令模型扩展到网络访问场景。NVMe/TCP 与基于 RDMA 的传输都是常见选择。此时主机一侧有发起端,远端系统提供控制器与命名空间,数据要经过网卡、交换网络和目标端。队列与完成的基本思想仍有帮助,但本地 PCIe 门铃与 DMA 的描述不能原样搬成所有网络传输的逐步实现。

网络让存储可以集中部署,也增加了新的边界。25 Gb/s 链路换算成原始字节速率约为 3.125 GB/s,尚未扣除网络与传输开销。即使远端 SSD 能读到 6 GB/s,单条这样的网络路径也不可能把它的全部带宽交给主机。网络拥塞、丢包、连接重建、目标端 CPU 忙碌都可能提高延迟。排查 NVMe-oF 时,主机磁盘统计之外还要看网卡、交换机和目标端。

本地设备故障常见于链路、供电、控制器或介质;远端存储还可能出现“设备健康但路径不可达”。多条路径能够提供额外的访问选择,但它们是否经过独立网卡、交换机与电源,需要按拓扑核对。2 条逻辑路径共享同一块故障网卡,并不构成完整的故障隔离。多路径切换期间的等待时间也会影响应用,因此不能只验证正常时能连上,还应约定异常时允许停顿多久。

选择本地还是远端,应从机器形态与需求出发。单机低延迟工作盘可以优先评估本地 NVMe;需要集中容量管理与跨服务器分配时,可以评估 NVMe-oF。它们都不自动提供备份、共享文件系统或多机写入协调。多台主机能看到某个块地址空间,不代表可以随意同时挂载普通文件系统写入,这需要上层明确支持相应访问方式。

4 动手:读一张控制器能力清单

先读能力,再解释测量结果。练习可以在已有系统上进行只读查询;若没有真实设备,也可以使用下面的教学清单。不要为读取能力去格式化命名空间或改变扇区格式。Linux 上常见的查询工具是 nvme-cli,设备命名与权限要求随系统而异。先用 nvme list 对照型号、序列标识与容量,再确认要查询的控制器和命名空间,避免把系统盘与测试盘混淆。

例如,对已确认的示例设备执行 nvme id-ctrl /dev/nvme0 与 nvme id-ns /dev/nvme0n1,分别读取控制器与命名空间信息。路径仅是示例,不能照搬为本机事实。前者帮助认识控制器能力,后者用于了解逻辑块格式、容量等信息。nvme smart-log /dev/nvme0 可补充温度、健康告警等观测。这些查询各自回答不同问题,能力清单不等于当前运行状态报告。

教学清单项目示例读数应怎样解释
控制器标识支持某个 NVMe 版本确定接口背景,具体功能仍逐项核对
命名空间当前逻辑块大小4096 B请求偏移和长度需要满足设备及软件路径的对齐约束
MDTS 解码后的限制128 KiB结合控制器页大小参数解释后的单命令传输上限
易失性写缓存能力支持还需查询当前配置,不能据此断言持久化行为
实际协商链路PCIe 4.0 ×4是当前连接状态,应与插槽和设备能力比较
温度与健康状态处于正常范围只是当前快照,持续负载仍可能改变温度

这张表特意把来源不同的信息放在一起练习解释。实际协商链路通常需要从 PCIe 信息或系统接口读取,并不是 id-ctrl 的某个通用字段。队列容量上限也可能来自控制器寄存器,而操作系统当前分配的队列数量、每条队列深度还受驱动与资源限制。报告应保留信息来源,不要把所有数值都笼统写成“NVMe 参数”。

遇到编码字段,要先确认单位。某些大小字段用从 0 开始的编码表示数量,另一些字段用指数和页大小共同表达限制。MDTS 不能把原始整数直接当成 KiB;如果值为 0,也要按规范理解其含义,而不是当成传输大小为 0。入门阶段优先使用工具的解码输出,并保留原始字段供核查。工具显示“支持”时,还要继续区分设备能力、当前启用状态和应用实际使用方式。

把查询结果压缩成可判断的结论才算完成练习。例如:“当前链路符合预期;逻辑块大小已确认;测试使用 4 KiB 请求,满足对齐;工具的大请求可能被软件拆分;持久化测试仍需核对缓存与同步语义。”这样的结论能指导后续操作。只抄一页十六进制字段,既不能证明性能正常,也不能证明配置满足需求。

把能力、状态与测量放到同一张观察表
把能力、状态与测量放到同一张观察表

5 多队列与中断亲和的取舍

多队列的价值在于减少多个 CPU 核心挤在同一个提交入口的机会。驱动可以让不同 CPU 使用不同队列,并把完成处理分配到合适的核心。对并行请求较多的服务器,这有助于降低共享数据结构的竞争。但队列不会自行产生有用工作。只有 1 个线程持续“提交 1 次、等完成、再提交”的应用,即使设备支持大量队列,也不会因此出现足够的在途请求。

中断亲和描述中断倾向由哪些 CPU 核心处理。请求在某个核心提交,完成却总由遥远的核心处理,可能增加缓存与跨插槽通信开销;如果 SSD 连接在某个 CPU 插槽下,而数据缓冲区主要位于另一个插槽的内存,也可能让数据路径变长。这里的“远近”是机器拓扑关系。双路服务器有 2 个 CPU 插槽,不等于任意线程访问所有设备与内存的代价都相同。

不过,把所有中断都绑到离设备最近的 1 个核心也可能制造瓶颈。该核心需要同时处理提交、完成和应用计算,繁忙时会推高等待。另一种极端是把工作散到很多核心,增加迁移与协调成本。实际选择应结合业务线程、CPU 拓扑、网卡中断和系统自动平衡机制。先观察每个核心的负载与中断分布,再做有限调整,比照抄固定绑定表可靠。

中断合并与轮询体现了另一组取舍。适当合并通知可以降低中断处理开销,但等待聚集的过程可能增加延迟。轮询可以更及时发现完成,却持续占用 CPU,是否划算取决于延迟目标与可用核心。共享服务器上的应用可能更需要把 CPU 留给计算,专门的低延迟节点则可能愿意为更稳定的完成发现时间投入核心。没有脱离负载的统一最佳设置。

队列深度也会改变结果。深度太低,设备等待主机提供工作;深度增大后,更多操作能够重叠,带宽可能提高。到达设备或链路上限后,继续增加深度往往主要增加等待时间,尤其会拉长尾延迟。交互式业务关注 p99,也就是约 99% 请求能在该时间内完成;批量搬运可能更关注持续带宽。相同硬件可以因目标不同而采用不同的并发设置。

调整时先固定请求大小、读写比例、工作集和测试持续时间,再逐步改变深度,记录带宽、IOPS、平均延迟、p99 与 CPU 使用率。只有这些条件一致,曲线才有比较意义。下面的滑块使用简化模型展示“并发先补足供给,随后增加排队”的趋势。它能帮助建立直觉,不能据此推算真实设备的 p99,也不能替代现场测量。

6 常见坑:把标称 IOPS 当应用吞吐

IOPS 是每秒完成的 I/O 操作数,带宽是每秒传输的数据量。在固定请求大小的情况下,两者可以换算:带宽约等于 IOPS 乘以每次请求字节数。100000 IOPS 的 4 KiB 读取,约对应 409.6 MB/s;同样的操作数若每次传输 128 KiB,所需带宽就大得多,实际可能先撞上链路或设备限制。只比较 IOPS 数字而不写请求大小,会把不同工作混成同一指标。

应用的 1 次操作也不一定等于设备的 1 次 I/O。读取文件可能命中内存缓存,根本没有访问 SSD;更新少量业务数据可能引起数据页、日志和元数据的多次写入。请求还可能在软件层合并或拆分。因此,设备完成 100000 次块操作,不代表应用每秒处理 100000 次事务。报告应同时写明应用指标与块设备指标,再解释它们之间为什么出现差距。

标称性能通常对应特定请求大小、并发、读写比例与设备状态。随机读取、顺序读取、同步写入和混合读写并不是同一种负载。短时间写入还可能主要落在设备的快速缓存区域,持续写入则暴露缓存耗尽后的能力。接近满盘、后台回收、温度上升都可能改变结果。入门测试至少区分突发与持续表现,并记录测试前后的设备状态。

单位混淆也会制造假故障。网卡常用 Gb/s,磁盘带宽常用 GB/s 或 GiB/s,容量也可能采用不同基数。8 bit 等于 1 B,但还要计入编码与协议开销。看到 25 Gb/s 网络只传出约 2.8 GB/s,不能仅凭数值小了近 10 倍就断言网络严重异常。应先统一单位,再与有开销的可达范围比较,同时检查发送端与接收端是否都具备相应能力。

另一个坑是把总 CPU 使用率当作没有 CPU 瓶颈的证据。64 个逻辑 CPU 中,1 个核心忙满,总体比例仍然很低;恰好负责提交或完成的核心却可能已经限制整条路径。也不能把设备的忙碌百分比简单当成内部所有并行资源都已用满。多队列设备的统计含义要结合工具解释,用带宽、延迟、在途数量与每核负载互相印证。

异常时,先保留证据,再决定是否重置。超时可能来自介质处理、控制器停顿、PCIe 链路问题,也可能是网络路径或主机调度问题。错误日志、内核消息、温度变化和发生时间能帮助缩小范围。控制器重置会影响它承载的命名空间与正在执行的请求;盲目重复重置既可能扩大业务影响,也可能丢失现场。没有看到明确完成的写入,还要由上层确认结果,不能直接假定安全重试。

从现象逐层缩小故障范围
从现象逐层缩小故障范围

7 案例:队列深度不足限制带宽

某台单路服务器安装 1 块 PCIe 4.0 ×4 SSD,用于读取已经写入的训练数据分片。测试工程师看到规格表中持续顺序读取约 6 GB/s,却只测到约 0.82 GB/s。当前链路确认为 PCIe 4.0 ×4,温度稳定,未发现健康告警。读取对象是专用测试文件,使用直接 I/O,工作集超过可用内存缓存;测试没有覆盖原始系统盘,也没有包含写入操作。

负载使用 128 KiB 请求,只有 1 个作业,每次等待读取完成后才提交下次请求,也就是实际深度约为 1。观测到平均完成延迟约 160 μs。在稳定条件下,可以用“平均在途数量约等于完成速率乘以平均延迟”做数量级核对。反过来,深度 1 除以 160 μs,得到约 6250 IOPS;再乘 128 KiB,得到约 819 MB/s,与观测接近。

这个计算不证明设备最高只能达到该速度,而是说明当前并发足以解释结果。CPU 等待上次请求期间,没有准备好足够的后续工作供设备重叠处理。工程师保持文件、请求大小、读写模式和测试时长不变,改用支持异步提交的读取方式,将目标深度逐步提高。每轮还确认工具报告的实际深度分布,避免参数写成 8,运行时仍主要停在 1。

实际在途水平平均延迟读取带宽教学观察
约 1160 μs0.82 GB/s主机提供的并发不足
约 4180 μs2.91 GB/s更多请求重叠,带宽增加
约 8200 μs5.24 GB/s接近本负载的有效工作区间
约 16350 μs5.99 GB/s带宽接近平台,等待增加

这些是用于解释关系的教学读数,真实设备不会必然沿着这组数值变化。尤其不能把深度 1 的延迟固定下来,直接推算深度 16 的带宽。并发增加后,控制器与介质的排队都会改变平均延迟。表中的每行使用自身的平均延迟,才能通过在途数量关系互相核对。尾延迟还需要单独测量,不能从平均值计算出来。

最终选择也不一定是深度 16。如果应用每次读取后有较长计算,预取 8 个分片可能已经满足训练流水线,并节约缓冲内存;如果目标是尽快搬完大文件,深度 16 的带宽更有价值。对有严格响应时间要求的业务,还需比较 p99 是否合格。该案例的结论是“在当前请求大小与路径上,并发不足限制带宽”,不是“所有慢盘都应提高深度”。

若增加深度后带宽不变,应沿着证据继续分支:实际并发是否增加,提交核心是否满载,链路是否降速,设备是否进入温度限制,远端场景是否受网卡限制。若出现错误或明显抖动,就先处理异常。性能调参只能解释健康路径的取舍,不能用更长队列掩盖掉线、超时与数据完整性问题。

8 把协议能力写进存储方案

方案中的“使用 NVMe SSD”只能作为起点。应继续写清服务器形态、可用槽位、设备外形、PCIe 代际与通道数、背板支持、供电和散热条件。机箱支持热插拔盘位,也不能据此推断任意设备都能在线拔插;必须确认整条平台路径、驱动与维护流程支持。固定在主板上的 M.2 与前置可维护盘位,在更换停机时间上可能有显著差别。

性能条目应描述负载与验收边界。例如:“每台主机使用 128 KiB 顺序读取,工作集大于内存缓存,在约定并发下持续运行 30 分钟,记录稳定阶段带宽、p99 与 CPU 占用。”随机小块业务则另列 4 KiB、读写比例和持久化要求。不要把多个互不相容的峰值拼成同一时刻必须达到的指标,也不要省略设备使用状态、文件系统与测试范围。

可靠性条目要把写入语义、故障恢复和数据保护分别说清。需要掉电后保留哪些已经确认的数据,应用如何发出同步要求,设备是否具备适合该目标的掉电保护,供电与缓存设置如何验证,都应有对应依据。命令超时后由谁判定失败、多久切换路径、恢复后怎样检查一致性,也属于方案内容。只写“支持 NVMe”无法回答这些问题。

远端方案还需补齐网络拓扑与共享边界。列明网卡速率、交换路径、目标端容量与计算资源,以及多台主机竞争时的服务目标。若使用多路径,要说明哪些故障可以被隔离、哪些仍是共享故障点。对共享命名空间的访问权限与上层协调方式也要明确,避免把连通性测试通过误认为并发写入安全。

最后,定义可持续的观测和变更方式。保留固件、驱动与系统版本,建立温度、错误、寿命相关指标和应用延迟的基线;升级后在受控范围复测。容量增长时同时检查耐久度、带宽和重建窗口,不能只按剩余空间扩盘。方案应允许后来者从需求追到配置、从配置追到测试结果,这样“协议能力”才成为可核查的工程承诺。

9 从 NVMe 走向 RAID 与存储阵列(H30)

到这里,我们讨论的主要是主机怎样向存储控制器提交命令,以及怎样确认数据路径是否发挥预期能力。这仍然没有解决“1 块设备坏了怎么办”。NVMe 可以报告错误、提供管理能力,也可以在合适系统中配合多路径访问,但这些并不自动产生数据副本。1 块速度很快的设备,依然可能成为容量、可靠性或维护窗口上的单点。

RAID 与存储阵列把问题向上推进:怎样把多个设备组织成更大的逻辑存储,怎样用镜像或校验在部分设备故障时保留访问能力,又要付出多少容量、写入与恢复开销。下一课将研究这些基本取舍。本课只建立边界:NVMe 描述访问存储的接口与命令,RAID 描述多个存储成员的组织方式;采用 NVMe 的系统仍需选择数据保护结构。

把多个设备组合后,原先的测量习惯仍然有用。阵列对主机完成了 1 次写入,内部可能涉及多个成员操作;阵列标称总带宽也可能被上行链路、控制器或网络限制。成员故障后的重建会消耗资源,正常时测出的性能不一定覆盖降级状态。验收需要同时关心健康运行与故障恢复期间的表现,而不是简单把单盘峰值乘以设备数量。

还有 2 个概念应提前分开。多路径通常为同一份存储提供多条访问路径,不能直接当成多份数据;RAID 可能容忍部分成员故障,也不能直接当成备份。误删除、软件错误以及超出保护范围的故障仍需要其他恢复手段。带着“路径、成员、数据副本分别在哪里”的问题进入 H30,就能更清楚地读懂阵列结构与故障边界。

放进方案里

为 1 台读取训练分片的机架服务器选择本地存储时,先把需求写成可测量的条件:数据集持续增长,主要负载是 128 KiB 顺序读取,目标持续带宽为 5 GB/s,应用可以使用预取,并允许占用一定缓冲内存。另列系统盘与数据盘,确认测试只读取预先准备的专用文件。容量、耐久度与数据保护需求单独评估,不能因为读取快就省略这些条件。

硬件清单写明数据盘采用 NVMe、PCIe 4.0 ×4,以及与机箱盘位匹配的形态。安装后核对实际协商链路、命名空间逻辑块大小、当前缓存配置与温度状态。若插槽共享上行链路,要把同时工作的网卡与其他存储纳入测试。双路平台还应记录设备连接的 CPU 插槽,避免应用与缓冲区的位置无意中拉长访问路径。

验收使用同一数据集与直接 I/O,逐步测试深度 1、4、8、16,记录实际在途分布、带宽、平均延迟、p99 和每核负载。持续运行 30 分钟,观察升温后是否仍达到目标。若深度 8 已接近目标且延迟合格,就把它作为应用预取的候选值;若只有更高深度才能达标,再评估额外内存与等待是否可接受,不能只挑最好看的瞬时读数。

维护条目约定超时与错误的采集位置、告警阈值及更换流程。对已确认写入的持久化要求,应由应用同步方式、设备缓存行为和掉电保护共同说明。盘故障后的业务恢复依靠明确的数据副本与恢复流程;需要阵列时继续评估 H30 的保护方式。验收附件保留命名空间标识、测试前后的错误日志与链路状态;若出现控制器重置或链路降速,应查明原因并复测,不能仅凭平均带宽达标签收。交付材料保存版本、拓扑、配置和测试条件,使后续更换设备或升级固件后能够复现验收。

10 小结:下一步 RAID 与存储阵列(H30)

NVMe 把存储请求组织成命令、提交队列与完成队列;PCIe 与网络传输决定命令和数据经过怎样的连接。性能判断需要同时看请求大小、实际并发、延迟、链路与 CPU,持久化判断则必须回到命令语义和缓存保证。

可以用 3 个问题自检:为什么 M.2 外形不能证明设备使用 NVMe?为什么队列深度提高后带宽会先增长再趋于平台?为什么命令完成、多路径与数据备份不能互相替代?下一课 H30 将把单个存储设备放进 RAID 与存储阵列,继续讨论容量、性能与故障恢复的取舍。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务