
我在搭建香港节点的高性能数据接入平台时,遇到一个性能瓶颈:服务器上挂载的企业级 NVMe 固态盘标称 7 GB/s 的顺序读写带宽,但在虚拟化场景下(特别是在 KVM/QEMU 虚拟机中),始终只能跑到 34 GB/s,而且 CPU 占用异常偏高,I/O wait 波动剧烈。调研发现这是传统 virtio-blk/scsi 和 I/O stack 引入过多软件中断与拷贝路径的结果。为了解决这个问题,我最终落地了PCIe Passthrough + SPDK 用户态驱动栈的组合方案,效果显著:顺序读带宽稳定跑到 6.87.2 GB/s,且 CPU 占用压缩在 20% 以内。
下面是我完整的技术实现路径。
一、整体方案架构
核心技术选型如下:
- 硬件:香港自营物理机,配三星 PM9A3 U.2 NVMe,PCIe 4.0 x4
- 虚拟化平台:KVM/QEMU + libvirt
- 操作系统:Debian 12(主机、虚拟机均相同)
- 直通方式:VFIO PCI Passthrough
- 存储栈:SPDK 24.x(绑定 VFIO 用户态 NVMe 驱动)
架构图概览:
[裸机 BIOS 启用 VT-d]
↓
[Linux Host VFIO 绑定 NVMe 控制器]
↓
[QEMU VM PCIe 直通 NVMe 控制器]
↓
[VM 内运行 SPDK 用户态 I/O Stack]
↓
[应用侧 Zero-Copy I/O 调度]
二、PCIe 直通配置(Host 端)
1. 开启 IOMMU 支持
编辑 grub 配置启用 Intel VT-d:
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt"
update-grub
reboot
验证是否生效:
dmesg | grep -e DMAR -e IOMMU
2. 查找并解绑 NVMe 控制器
确认目标设备:
lspci -nn | grep -i nvme
例如返回:
81:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller PM9A3 [144d:a80a]
绑定 VFIO:
modprobe vfio-pci
echo 144d a80a > /sys/bus/pci/drivers/vfio-pci/new_id
echo 0000:81:00.0 > /sys/bus/pci/devices/0000:81:00.0/driver/unbind
echo 0000:81:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
3. 配置 libvirt 虚拟机 XML
在 VM 的 XML 文件中添加 PCIe Passthrough:
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x81' slot='0x00' function='0x0'/>
</source>
</hostdev>
重启虚拟机后,lspci 里应能识别 NVMe 控制器。
三、虚拟机内配置 SPDK
1. 安装依赖与内核准备
apt install -y build-essential libnuma-dev libaio-dev libpci-dev liburing-dev
安装 DPDK 和 SPDK:
git clone https://github.com/spdk/spdk.git
cd spdk
git submodule update --init
./configure --with-vfio-user
make -j$(nproc)
2. VFIO 绑定 NVMe 设备(VM 内部)
scripts/setup.sh
确认 NVMe 显示在 /dev/nvmeX,但SPDK 会直接通过 VFIO 访问,无需内核块设备驱动。
3. 启动 spdk_nvme_perf 测试性能
build/examples/spdk_nvme_perf \
-q 128 -s 4096 -w read -t 60 -r 'trtype:PCIe traddr:0000:81:00.0' -c 0xF
输出中:
IOPS: 1750000
Bandwidth: 7.0 GB/s
CPU core 0~3 usage: ~20%
四、优化点细节说明
1. CPU 亲和性绑定
通过 taskset 或 SPDK 的 -c 参数将 I/O 线程绑到 VM 的高性能核心(NUMA 亲和)。
2. 关闭透明大页、irqbalance
主机和 VM 内均执行:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
systemctl stop irqbalance
3. hugepage 优化
主机和虚拟机都配置 hugepage:
echo 2048 > /proc/sys/vm/nr_hugepages
SPDK 默认使用 2M hugepages,通过 DPDK 实现大页内存分配与 DMA 映射,减少 TLB miss。
4. 多线程并发调度
在生产系统中,我使用 SPDK 的 RPC 服务端口将其接入业务层的用户态线程池中,避免使用 read/write 系统调用,并改造为异步 DMA I/O 调用。
五、实测结果与总结
在香港服务器的生产环境上,采用 PCIe Passthrough + SPDK 后,IO 测试结果如下:
| 测试项 | 改造前(virtio-blk) | 改造后(SPDK) |
|---|---|---|
| 顺序读带宽 | 3.2 GB/s | 7.1 GB/s |
| 顺序写带宽 | 2.8 GB/s | 6.9 GB/s |
| CPU 占用率 | 60%+ | 18~22% |
| IO latency | 60us+ | 12~20us |
这个改造方案对我们香港高吞吐、高并发的数据节点极为重要。在确保带宽利用最大化的同时,也大幅度降低了系统资源占用,为后续并发计算腾出 CPU 空间。未来也计划结合 RDMA 和 NVMe-oF,在节点间横向扩展。
如你也面临虚拟化存储性能瓶颈,特别是在香港这类南向出口压力较大的机房场景中,这套组合方案是值得落地实测的。











