在 PVE 环境里,很多看似“应用层”的卡顿,其实都根源于磁盘 I/O。尤其是 Android 容器、自动化脚本、Unity 热更、数据库、编译任务这类工作负载,最容易遇到的问题不是 CPU 或内存不足,而是 iowait 长时间居高,甚至超过 50%。
这些场景的典型特征是:
- 频繁写入 4K 左右的小文件
- 运行中反复解压、校验、更新配置
- 大量同步落盘的
fsync/flush请求 - 虚拟机内线程被迫等待底层存储队列
如果底层存储类型和虚拟磁盘参数选择不当,QEMU 主事件循环很容易被写请求卡住,最终表现为虚拟机内所有进程都在等待 I/O。
本文将从“底层存储选型 → 虚拟硬盘高级参数 → 推荐配置组合”三条主线,给出一套适合 PVE 的实战化调优方案。
一、先讲结论:底层存储首选 LVM / LVM-Thin
PVE 支持的存储后端大致可以分成两类:
- 文件系统目录型:Directory / NFS / CIFS
- 裸块设备型:LVM / LVM-Thin
其中,裸块设备型在虚拟机场景中通常明显更优,因为它能减少一层“文件系统 → 镜像文件 → 宿主文件系统”的额外翻译和元数据处理。
1. 块存储比目录存储更省事
如果使用 Directory 模式,虚拟机磁盘本质上是在宿主机已有文件系统中创建 .qcow2 或 .raw 文件。这样每一次虚机 I/O 都要经历:
- 虚机文件系统
- QEMU 镜像驱动
- 宿主机文件系统
这会带来额外的元数据更新、文件索引解析和写入放大。对于随机小文件写入尤其不友好。
而 LVM 直接把逻辑卷作为裸块设备透传给 QEMU,能够消除中间层的虚拟化抽象损耗,磁盘行为更贴近真实物理介质。
2. 标准 LVM 和 LVM-Thin 的区别
| 评估维度 | 标准 LVM(Thick) | LVM-Thin(精简制备) |
|---|---|---|
| 空间分配 | 创建时即预留完整物理空间 | 按需分配,写多少占多少 |
| 快照与克隆 | 支持,但写入时容易有明显性能滑坡 | 原生支持快照和克隆,性能影响很小 |
| 存储超卖 | 不支持 | 支持,适合模板/批量部署 |
| I/O 吞吐 | 最纯粹,几乎无额外开销 | 仅在首次写新块时有轻微元数据开销 |
| 空间回收 | 依赖复杂操作或不支持 | 支持 TRIM / Discard,回收比较自然 |
| 运维风险 | 低,逻辑简单,故障边界清晰 | 存在超卖爆池风险,需确保池容量规划 |
因此可这样判断:
- 追求绝对纯性能、磁盘空间充足、很少做快照:首选标准 LVM
- 需要快照、模板克隆、共享存储池、日常运维灵活:LVM-Thin 更平衡
对多数“高并发碎文件写入 + 需要长期维护”场景来说,LVM-Thin 是最实用的选择。
二、PVE 虚拟磁盘高级参数要重点看这几项
在 PVE Web 界面中,选中虚拟机 → 硬件 → 双击硬盘(例如 Hard Disk (scsi0))→ 右下角勾选 “高级(Advanced)”,这里有几个参数对性能影响非常大。
1. 缓存策略:最关键的决策点
缓存策略决定数据在以下几层之间的同步时机:
- Guest OS(虚拟机内)
- Host OS(PVE 宿主机内存页缓存)
- Physical Disk(底层物理盘)
常见选项包括:
默认 / 无缓存:Default (No cache) / cache=none
- 机制:使用 Linux
O_DIRECT,绕过宿主机的页缓存 - 特点:性能接近裸盘,宿主机不额外占用 RAM
- 适合:多数生产环境,尤其是数据库和关键业务虚拟机
Direct sync:directsync
- 机制:同时启用
O_DIRECT和O_DSYNC - 特点:每次写入都必须等待底层存储确认后才返回
- 适合:极端安全要求场景,但随机小写性能非常差
Write through:writethrough
- 机制:开启读缓存,但写入时必须同步落盘
- 特点:读性能不错;写性能受制于物理落盘速度
- 适合:读多写少、对数据一致性要求高的系统
Write back:writeback
- 机制:写请求先进入宿主机内存缓存,而不是立刻刷盘
- 特点:适合缓冲高峰写入;但如果主机断电,未刷盘数据可能丢失
- 适合:开发环境、标准测试环境、部分缓存型业务
Write back(不安全):unsafe / cache=unsafe
- 机制:在
writeback的基础上,忽略客户机发出的fsync/Flush指令 - 特点:性能极强,能明显降低因为频繁
fsync导致的阻塞和iowait - 风险:如果宿主机突然断电或崩溃,可能出现脏页损坏或数据丢失
- 适合:挂机机、Android 容器、编译节点、可重建/无状态系统
缓存模式横向对比
| 缓存选项 | 宿主读缓存 | 宿主写缓冲 | 响应 fsync | 断电安全性 | 小文件随机写性能 |
|---|---|---|---|---|---|
| 默认 / 无缓存 | 否 | 否 | 是 | 极高 | 中等 |
| Direct sync | 否 | 否 | 强制每次落盘 | 极高 | 非常差 |
| Write through | 是 | 否 | 是 | 高 | 偏慢 |
| Write back | 是 | 是 | 是 | 中等 | 良好 |
| Write back(不安全) | 是 | 是 | 直接忽略 | 极低 | 极佳 |
关键结论:如果你的虚拟机是“高频随机写 + 需要极低卡顿”的挂载场景,
Write back (unsafe)往往是唯一能真正缓解瓶颈的选择;但它只适用于对数据可恢复性要求不极端的环境。
2. 丢弃(Discard)
- 机制:透传 SCSI UNMAP / TRIM 指令
- 建议:务必开启
- 作用:当虚拟机内删除文件时,自动通知底层 LVM-Thin 或 SSD 回收空间,避免存储池“虚胖”
对 SSD 和 LVM-Thin 来说,这个功能非常重要。否则长时间运行后,存储池看起来“有空间”,但实际上文件系统和底层介质都在不断积累无效空洞。
3. IO thread(独立 I/O 线程)
- 机制:让虚拟机磁盘 I/O 不再全由 QEMU 主事件循环处理,而是交给独立内核线程
- 建议:务必开启
- 重要前提:同时将虚拟机 SCSI 控制器设为
VirtIO SCSI single
如果不启用 IO thread,磁盘的随机写入很容易占满 QEMU 的主事件循环,造成网络、CPU 虚拟中断、线程调度都被卡住。对于高并发小文件写入场景,这通常就是根因之一。
4. SSD 仿真(SSD Emulation)
- 机制:告诉虚拟机这是非旋转介质,
rotational=0 - 建议:务必开启
- 作用:让客户机使用更适合 SSD 的调度策略,同时打开 TRIM / Discard 相关支持
效果是让 Linux 客户端直接进入更低延迟、队列更友好的 IO 调度模式,比如 none 或 mq-deadline。
5. 异步 IO(Async IO)
- 建议:保持默认值
io_uring - 作用:在新内核中借助双环缓冲区实现更低延迟的异步 IO
- 一般不需要手动修改,默认值通常比传统
threads/native更现代、更高效
三、推荐的落地配置清单
针对 Android 模拟器容器、自动化测试、脚本挂机、编译构建等高并发碎文件写入场景,推荐组合如下:
| 配置项 | 推荐值 | 生效位置 | 作用 |
|---|---|---|---|
| 底层存储 | LVM / LVM-Thin | PVE 存储后端 | 直接裸块设备,减少抽象层损耗 |
| 控制器类型 | VirtIO SCSI single | 虚机硬件 → SCSI 控制器 | 让每个磁盘拥有独立队列 |
| 缓存策略 | Write back (不安全) | 虚机硬盘高级参数 | 消除高频 fsync 对 QEMU 的卡顿 |
| 丢弃(Discard) | 开启 | 虚机硬盘高级参数 | 维持 SSD 和 LVM-Thin 的空间回收 |
| IO thread | 开启 | 虚机硬盘高级参数 | 分离磁盘 I/O,避开主事件循环瓶颈 |
| SSD 仿真 | 开启 | 虚机硬盘高级参数 | 让客户机更适配 SSD 工作模式 |
| 异步 IO | 默认(io_uring) | 虚机硬盘高级参数 | 更高并发下的低延迟 IO |
关键注意事项
修改上述参数后,单纯在虚拟机内部执行 reboot 往往还不够。因为 QEMU 是在启动时根据硬盘参数构建命令行的,必须在 PVE Web 界面中执行一次“彻底关机(Stop)”,再重新启动(Start)才会生效。
四、适用场景与取舍原则
适合使用“高性能写缓存”方案的场景
- Android 容器 / Redroid
- 自动化脚本挂机
- 游戏热更、资源解压、补丁更新
- 编译构建环境
- 可重建的缓存型应用
这些场景最怕的不是“写入失败”,而是“写得太慢导致整个系统卡死”。在这类场景中,Write back (unsafe) 最大的价值就是把小文件随机写入从 QEMU 主线程的阻塞路径里剥离出来。
不适合使用该方案的场景
- 数据库主库
- 金融系统
- 关键业务的生产库
- 任何不能接受断电丢数据的系统
对于这些环境,更稳妥的做法是:
- 底层使用 LVM / LVM-Thin
- 禁用不安全缓存
- 选择
Default (No cache)或Write through - 打开 IO thread + Discard + SSD 仿真
五、实战效果:为什么它能明显改善 iowait
最常见的问题并不是“磁盘真的不够快”,而是“QEMU 把大量写入都压进了单线程主循环”。一旦出现高频 fsync,主事件循环就会被卡住,后续网络、屏幕响应、CPU 调度都会被连带拖慢。
启用以下配置后:
IO thread把磁盘 I/O 从主线程剥离VirtIO SCSI single提供独立控制器队列Write back (unsafe)对高频fsync做缓冲Discard、SSD 仿真让底层存储保持更好的长期表现
这会使高并发随机写场景中的系统状态明显改善:
iowait从长期高位回落到安全区间- 操作界面不再明显卡顿
- 热更、解压、校验等任务执行时间缩短
- 虚拟机更稳定地维持“有效工作”状态
六、最终建议
如果你是在 PVE 里跑“高并发小文件 + 频繁同步写入”的工作负载,最优实践可以概括为:
- 选择 LVM / LVM-Thin 作为底层存储
- 虚拟机使用
VirtIO SCSI single控制器 - 磁盘启用
IO thread和Discard - 打开
SSD Emulation - 在适合的场景下使用
Write back (unsafe)作为性能增益手段 - 对关键生产系统保守使用
Default (No cache)/Write through
一句话总结:
“想把 PVE 里的磁盘卡顿彻底压下来,关键不在于堆更大更快的盘,而在于去掉中间层的阻塞,并把高频写入从 QEMU 主线程中放出来。”
如果你在 PVE 里跑的是 Android 模拟器、自动化挂机、编译容器或大规模资源热更,以上配置组合通常会比单纯换一块更快 SSD 更有效。