外观
精简置备与空间回收:discard 与 TRIM
给虚拟机分配了 64 GB 磁盘,但存储上实际只占用了 8 GB——这就是精简置备 (thin provisioning)。它很方便,但也是「local-lvm 明明没写多少却报满」这类困惑的根源。这篇讲清楚它的原理,以及怎么通过 discard/TRIM 让删除的空间真正回收。
为什么需要它
精简置备的存储(LVM-Thin、ZFS、qcow2)只在客户机真正写入数据时才占用物理空间,分配的容量只是一个上限。好处是可以先分配得宽裕些,不用精确预估;代价是可以让多台客户机的分配总量超过物理容量(超额分配),一旦真的写满会出问题。
客户机在文件系统里删除文件,默认情况下底层存储并不知道——那些块在存储层依然算「已用」。TRIM/discard 就是让这个信息传回存储层的机制。
开始之前
- 确认你的磁盘总线用的是 VirtIO SCSI(推荐)或 VirtIO Block。IDE/SATA 总线通常也支持,但整体上不如 SCSI 总线灵活。
- 确认底层存储支持精简置备:LVM-Thin、ZFS、qcow2(在文件存储上)都支持;普通 LVM 和 raw 格式不支持,讨论 discard 没有意义。
- Windows 客户机额外需要开启 SSD Emulation,否则即使开了 Discard,Windows 也不会发 TRIM。
操作步骤
第一步:给磁盘开启 Discard
Web UI:虚拟机 → Hardware → 选中磁盘 → Edit → 勾选 Discard。
VirtIO Block 有内核版本要求
用 VirtIO Block 总线时,Discard 需要客户机内核 5.0 及以上。不确定就用 VirtIO SCSI,兼容性更好。
第二步:让客户机主动发 TRIM
只开 Discard 还不够,客户机自己也要发起 TRIM 请求,两种方式:
方式一:客户机里定期自动执行(推荐,持续生效)
sh
# 在客户机内部执行(Linux,以 Debian/Ubuntu 为例)
systemctl enable --now fstrim.timer
systemctl status fstrim.timerWindows 客户机装好 VirtIO 驱动后,系统自带的存储优化任务会按周期对识别为 SSD 的磁盘发 TRIM,通常不用额外配置。
方式二:从宿主机侧手动触发一次(需要 QEMU Guest Agent)
sh
# 宿主机 shell 执行
qm guest cmd <vmid> fstrim<vmid> 是虚拟机编号,从 Web UI 左侧树的客户机名称前看到。这条命令要求客户机已装好 qemu-guest-agent 并在 Options 里勾选了 Qemu Agent,见VirtIO 驱动与 QEMU Guest Agent。
磁盘搬迁和迁移后自动 TRIM 一次
Options → QEMU Guest Agent 里有一个 Run guest-trim 开关,开启后 PVE 会在磁盘搬迁(Move Disk)和使用本地存储的在线迁移之后自动对客户机发一次 TRIM,帮精简置备的目标存储回收空间。ext4 客户机注意:这类一次性 TRIM 之后如果没有重启,后续再触发只会覆盖上次之后变化的部分,不是每次都全盘扫描。
第三步:容器(LXC)的 discard
容器跑在宿主机文件系统之上,回收方式不同:容器根文件系统所在的存储(LVM-Thin、ZFS)本身按块管理,容器内部删除文件后,fstrim 在容器里同样可以调用,效果取决于底层存储是否为块级精简置备。
检查结果
- 看客户机侧:Linux 里
systemctl status fstrim.timer显示 active;或手动跑一次fstrim -av观察输出的 "trimmed" 字节数。 - 看存储侧变化:sh
# LVM-Thin:观察 Data% 是否下降 lvs -a # ZFS:观察数据集/zvol 的 USED 是否下降 zfs list - 做一次真实测试:在测试虚拟机里写入几 GB 数据再删除,跑一次 TRIM,对比前后
lvs -a或zfs list的占用变化,能看到明显下降就说明链路通了。
精简置备不是「空间变多了」,超额分配写满同样会出问题
不管有没有开 discard,精简置备允许分配总量超过物理容量。把使用率当成必须盯的指标:LVM-Thin 看 lvs -a 的 Data%,ZFS 看 zfs list 或 zpool list 的可用空间,超过 80% 就该清理或扩容,不要等到写满。写满 thin pool 会导致正在运行的客户机写入失败,文件系统可能损坏。详见 local 与 local-lvm。
常见问题
开了 Discard,lvs -a 的 Data% 却没降。 检查客户机里 fstrim.timer 是否真的在跑;再检查磁盘总线是不是 VirtIO Block 但客户机内核低于 5.0——换成 VirtIO SCSI 试试。
Windows 客户机 TRIM 不生效。 确认磁盘的 SSD Emulation 和 Discard 都勾了。VirtIO Block 不支持 SSD Emulation,如果用了这个总线,换成 VirtIO SCSI。
qm guest cmd <vmid> fstrim 报超时。 磁盘很大时 TRIM 本身要跑几秒到几分钟,命令可能先超时返回,但 TRIM 在客户机里可能仍在继续。去客户机系统日志确认执行情况,不要重复触发。
容器(LXC)要不要单独配置 discard? 容器磁盘直接建在 LVM-Thin 或 ZFS 上,回收依赖底层存储本身的精简置备特性,容器内部正常删除文件、必要时跑 fstrim 即可,不需要单独开关。