土法炼钢 · 系统与基础设施

【SPDK 用户态存储】对照内核路径:SPDK、内核 NVMe 与 O_DIRECT+io_uring

文章导航

分类入口
storagelinux
标签入口
#spdk#nvme#io_uring#o_direct#kernel#migration#co-location#v26.01

目录

选型争论若滑成「谁更快」,会立刻要一张没有口径的延迟榜。本系列前 14 篇给出的判决材料其实是另一类:一次 I/O 走哪条栈、失败落在哪、运维独占什么

本文是第 15 篇:对照三条常见本机路径——SPDK 用户态内核 NVMe 块层O_DIRECT + io_uring——并写共置与迁移成本。数字结论留给你的硬件与 第 13 篇 的合法口径;本站不发明排名。

本文是「SPDK / 用户态存储栈」系列第 15 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 14 篇 · 排障坐标系 五轴清单
第 15 篇 · 对照内核路径 机制差、共置、迁移税
第 16 篇 · 选型收束 排除树与开放问题

版本锚定:SPDK v26.01。内核路径细节外链 storage/79io_uring 系列storage/03;不重写内核全书。


一、三条路径各保证什么

flowchart TB
  subgraph spdk_path ["SPDK userspace"]
    app1["App / nvmf / vhost"] --> reac["reactor poll"]
    reac --> ubdev["bdev"]
    ubdev --> unvme["userspace NVMe"]
  end
  subgraph kernel_nvme ["Kernel NVMe"]
    app2["App"] --> vfs1["VFS / block"]
    vfs1 --> knvme["nvme driver"]
  end
  subgraph direct_uring ["O_DIRECT + io_uring"]
    app3["App"] --> uring["io_uring SQ/CQ"]
    uring --> vfs2["VFS + block"]
    vfs2 --> knvme2["nvme driver"]
  end
维度 SPDK 用户态 内核 NVMe(常规定稿) O_DIRECT + io_uring
完成模型 用户态轮询为主 中断 / 内核轮询等内核机制 完成通知进 CQ;仍经内核块层
设备占用 VFIO/UIO 独占常见 内核驱动管理,易与系统共置 同左(内核驱动)
块抽象 bdev(砍/改了内核 block 诸多策略) Linux block / mq 同内核块层
典型职责 存储目标、用户态栈、极致轮询延迟敏感面 通用 OS 存储 应用自管缓存时的高 IOPS 提交
运维方言 JSON-RPC、reactor、setup.sh sysfs、nvme-cli、块层 QoS io_uring 参数 + Direct 对齐

站内 79 已钉:O_DIRECT 解决缓存语义,io_uring 解决提交效率——二者不替代用户态驱动。SPDK 解决的是「驱动与块栈是否搬出内核」,不是「要不要 Direct」。

判决句:


二、共置:同机两条栈的真实约束

「一台机器上数据库走 io_uring,网关走 SPDK」听起来美好,硬约束是 设备与 CPU

  1. 同一 NVMe 不能同时被内核 nvme 与 SPDK vfio 友好共享(常规独占模型)。共置通常意味着:不同盘 分给不同栈,或接受卸载/重绑的切换窗。
  2. CPU:SPDK reactor 吃满专用核;io_uring 应用也要算力。共置时要显式切分 cpumask,否则互相偷时间,却在各自监控里都「很忙」。
  3. 大页与 IOMMU:SPDK 环境脚本会影响整机;容器/kube 设备插件策略要写进同一运维手册。
  4. 排障坐标加倍:第 14 篇清单必须标明流量进了哪条栈——否则「磁盘超时」会同时误导两边 on-call。

共置合法场景举例(机制层,非推荐清单):系统盘与日志盘留内核;少数数据盘交给 SPDK 做 nvmf target;管理面 SSH 与监控仍走内核网卡。非法幻想:同一命名空间块设备「有时 SPDK 有时内核」而无编排切换。


三、迁移税:从内核路径迁到 SPDK(或反向)

税项 内核 → SPDK SPDK → 内核
设备 bind vfio,应用改连 bdev/RPC/nvmf unbind、恢复 nvme,改回块设备路径
配置真相 学会 save_config / -c 与热 RPC 纪律 回到 fstab/LVM/多路径传统工具
观测 iostat RPC 方言 iostat/blktrace/nvme 日志
性能门禁 按第 13 篇自建口径;官方 report 仅方法参考 同理,勿沿用用户态 report 数字
人员 轮询 CPU 税与「高占用即正常」的认知切换 重新接受中断/块层语义

迁移失败模式常见于:只迁数据面、不迁配置库存与排障手册。进程一重启,RPC 热配置蒸发(第 12 篇),看起来像「SPDK 不稳」,其实是生命周期模型没迁完。

反向迁移(SPDK → 内核)往往发生在:付不起专用核、要与云主机盘/快照生态合流、或团队编制撑不住用户态运行时。机制上这不是「认输」,而是排除树下一格(第 16 篇)。


四、和协议层、异步模型层的分工

问题 去哪读
NVMe/NVMe-oF 命令与队列语义 storage/03
O_DIRECT 对齐与 io_uring 固定缓冲 storage/79
SQ/CQ、epoll 对照 io_uringos/57
用户态栈内延迟落点 本系列 02–10、14

本篇不比较「论文里用户态一定赢」——硬件代际、轮询核税、尾延迟与运维成本会改写结论。开放争论留给终章。


五、何时「看起来该上 SPDK」其实不该

下列动机经常把团队带进用户态,却经不起第 14 篇清单与排除树拷问:

  1. 只看到官方 Performance Report 的峰值曲线,没看到专用核与硬件代际——把外部方法抄成本机 SLO。
  2. 本机数据库盘已经在 O_DIRECT+io_uring 上可归因,却为了「技术先进」改绑 vfio,结果失去标准块工具链。
  3. 云主机盘 / 托管块本就不能 VFIO 独占,却按裸金属 SPDK runbook 推进,最终卡在环境轴。
  4. 想顺手做 NVMe-oF,但流量模型其实是同机容器互访——可能 vhost 或内核路径更贴,却先上了织物传输复杂度。
  5. 编制上无人维护 JSON 库存,把热 RPC 当长期配置,第一次滚动重启即漂。

对称地,下列信号说明内核路径可能正在成为瓶颈,值得认真评估 SPDK(仍不是自动切换):

评估动作应是:用第 13 篇口径在目标硬件上做可复现实验,并写下迁移税清单(第 15 篇第三节)是否有人签字买单——而不是先改生产绑盘。


六、谱系与工程间隙

谱系(简):NVMe 多队列把并行提交变成硬件常识 → 内核块层用 mq 与中断/轮询变体服务通用 OS → io_uring 把「完成通知」做进高效系统调用界面 → SPDK 把驱动与块抽象整体搬到用户态 poll。四者解决的痛点不同:硬件队列、OS 共用、syscall 摊销、绕过内核数据面。

工程间隙:论文或博客里的「用户态更短路径」常假设专用核、巨页、无共置干扰、且忽略控制面与升级。生产里这四项经常不同时成立。另一间隙是工具链:内核路径享受 blktrace、发行版多路径、云快照;SPDK 路径享受 RPC 热改与 poll 延迟,但快照/迁移/监控要自建。选型比较的是间隙能否被编制填上,不是形容词。


迁移演练应按「可回滚」设计:保留一张未绑定的对照盘走内核路径,先将只读或可丢流量切到 SPDK,再扩写流量。回滚剧本必须包含驱动 rebind 与库存停用,而不是假设「停掉 SPDK 进程盘就会自动回来」——设备归属不会因为进程退出就自动回到你期望的内核驱动状态,除非环境脚本明确做了对称的 teardown。把 teardown 与 setup 放在同一仓库、同一评审流,是迁移税里最容易被跳过却最贵的一项。

对应用改造成本也要单独立项:从打开内核块设备到对接 SPDK 库或改走 NVMe-oF,中间差的是整条 I/O 提交与完成语义。有的团队用 SPDK 只做 target、应用仍用内核 initiator,这是降低改造面的合法折中,但要把网络与传输轴重新纳入第 14 篇清单,不能假装「还是本地盘」。

容器与编排环境下,共置约束更严:设备插件、CPU manager、大页与 IOMMU 都必须同时说真话。只把 SPDK 进程塞进容器、却仍让 kubelet 与其他 Guaranteed pod 争抢同核,会得到「容器化成功、延迟目标失败」的结果。对照 storage/79 的路径时,记住 io_uring 应用也可以在同一节点争用 CPU——共置是资源账,不是只有 SPDK 才有的税。

最后,监管与合规场景可能强制「标准块设备可见」或特定加密/审计链路只认内核路径。此时机制优势让位于合规约束,排除树应在第一叶就走开。把合规当成事后补丁,只会在上线前夕把用户态栈整段拆掉。

从成本会计角度看,SPDK 的核税应计入「存储数据面专用容量」,不要从通用计算池偷核却仍按通用池报利用率。内核路径的中断与软中断占用则常被摊进节点共用账本,两种会计口径不同,直接比「CPU%」会得出相反结论。做选型材料时,建议同时报:有效带宽或 IOPS、专用核数、以及是否独占设备——缺一项就标「不可比」。

若业务同时需要快照、克隆、薄置备等企业阵列功能,要问这些功能落在哪一层:SPDK bdev 模块、上层分布式存储、还是云控制面。功能缺口不能用「用户态更快」填补;应回到排除树看是否该留在内核/云块生态。

教育成本也是迁移税:让习惯块层的工程师接受「CPU 100% 可能健康」,以及让习惯轮询的工程师接受「内核路径的可观测方言」,都需要演练预算。忽略这项会在排障时发生方言冲突,延长故障时间。

也可以把三条路径理解成不同的「所有权边界」:内核路径所有权在 OS 与发行版工具链;io_uring 组合路径所有权在应用与内核异步接口;SPDK 路径所有权在用户态进程与 RPC 库存。所有权不清时,SLA 与值班表都会悬空。

所有权边界写进值班表后,才能把五轴清单里的「找谁」从口头约定变成可执行路由。

值班路由不清时,再完整的机制对照表也只会停在文档里。

七、参考资料

官方与站内(A/B)

工程判断(标明)

上一篇:排障坐标系 · 系列目录 · 下一篇:选型收束

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .