先说一段我自己的经历吧。前几年接手过一套KVM虚拟化环境,里面跑着几十台业务虚拟机,磁盘清一色用的raw格式。当时也没人说得清为什么这么选,只说是“性能好”。结果某天宿主机磁盘报警,我查了一圈才发现,几台跑日志的空闲虚拟机,单个镜像文件占了几十GB,真实业务数据可能连2GB都不到——因为当初部署时直接按最大容量预分配了。后来跟另一套测试机上一对比,那边用的qcow2,同样标称100GB的磁盘,宿主机实际占用不到20GB。从那天起我就把一个道理记牢了:磁盘格式的选择不是拍脑袋的事,它直接影响你的性能、空间和运维方式。
这篇文章就围绕KVM下两个最常见的磁盘格式——qcow2和raw——把我这几年实际使用中积累的对比结论、性能测试数据、转换命令、踩坑记录全部摊开来聊。没有教科书式的长篇大论,全是能直接拿来用的经验。适合正在做KVM环境选型的朋友,也适合纠结“要不要把现有虚拟机磁盘格式统一”的运维同学。看完你至少能回答自己两个问题:我的场景到底该用哪个?如果格式不合适,怎么安全地转换?
1. 先搞清楚raw和qcow2到底是什么
在对比性能之前,得先明白这俩东西的本质。选格式的本质其实是选一种存储策略,不只是文件扩展名不同。
1.1 raw:最朴素的磁盘镜像
raw是所有虚拟磁盘格式里最直白的一种。它的内容严格等同于虚拟机的裸磁盘块,也就是说,你把它当成一块真实的物理硬盘来理解就完全没错。虚拟机里面写一个扇区,raw文件里对应位置就真的写入了这个扇区的数据,没有任何中间包装。
这种简单带来的最大好处是性能损失极低。宿主机向raw文件下发IO请求,几乎就是直接穿透到文件系统、再落到物理磁盘,少了层层元数据计算。坏处也很直接:不预分配的话,你得依赖文件系统层面的稀疏文件来节省空间;预分配的话,创建多大就占多大,哪怕里面全是空白。而且raw本身没有快照、压缩、加密这些花活,想要这些能力必须借助宿主机层面的LVM快照、文件级备份之类的机制。
1.2 qcow2:为虚拟化而生的格式
qcow2的全称是QEMU Copy On Write Version 2,它是QEMU/KVM自家的格式。名字里的“Copy On Write”是理解它的关键,qcow2天生就带按需分配的能力:你创建一个100GB的qcow2磁盘,它初始大小可能只有几百KB,虚拟机在哪个块上写了数据,这个块才会真正被分配出去。这种机制使得qcow2在空间利用上天生占优势。
qcow2另一项看家本领是快照。它支持内部快照,可以在运行中的虚拟机里直接创建一个时间点状态,之后随意回滚,这在开发测试场景里太好用了。另外它还支持zlib压缩和AES数据加密,这些功能虽然日常用得不算多,但一旦需要,raw就需要额外的工具链才能补齐。不过代价也明显——每个IO都要过一遍元数据查询和可能的碎片整理逻辑,性能上天然比raw多一层开销。
提示:qcow2不等于“慢”的代名词。在现代内核和QEMU版本里,配合合适的缓存模式和IO调度,很多场景下它的性能已经能追平raw。后面我会具体说。
2. 逐项对比:性能、容量、功能到底差多少
我这里不打算重复网上那些笼统的说法,直接给你看我实际测试和观察到的结果。
2.1 性能:raw是不是真的一边倒碾压
很多老运维选raw的理由就是“性能好”,这话只对了一半。我自己的测试经验是:在纯顺序读写场景下,raw和qcow2几乎没差别,因为qcow2的元数据开销在这种大块IO里被摊薄了;但在随机读写、尤其是小块随机IO下,raw确实能跑出明显更高的IOPS,差距根据磁盘类型和缓存配置不同,大概在5%到20%之间。
这个差距在机械硬盘时代是决定性的,因为磁盘本身寻道慢,虚拟层再多一点开销就会雪上加霜。但放到现在的NVMe固态盘上,磁盘本身延迟已经极低,虚拟化格式那点开销被大大稀释。我一个实际项目的经历是:同样一台跑小型数据库的虚拟机,从raw转成qcow2后,业务侧读写延迟大概涨了几毫秒,体感几乎察觉不到,但宿主机磁盘占用直接降了四成。
另外,缓存模式对性能影响比格式本身大得多。我对比过几组组合,见下表:
| 格式 | 缓存模式 | 顺序读 | 随机写 | 综合表现 |
|---|---|---|---|---|
| raw | none + native AIO | 高 | 高 | 最强 |
| qcow2 | none + native AIO | 高 | 中上 | 接近raw |
| qcow2 | writeback | 中高 | 中 | 稳定性一般 |
| raw | writeback | 中高 | 中 | 浪费raw优势 |
所以我的结论是:如果你的宿主机全是固态盘,且虚拟机数量不多,选qcow2完全不用慌性能;如果你跑的是读写密集型的生产库,又追求极致IOPS,那raw仍然值得。
2.2 磁盘占用:小文件块的省钱逻辑
这点是qcow2最大的护城河。准备创建磁盘时,raw如果按最大容量预分配,比如100GB,那么宿主机上立刻少100GB;如果不预分配,那就得手动创建稀疏文件,而稀疏文件在数据写满后会持续占空间,还容易遇到文件系统碎片。qcow2则完全是动态增长的,创建时几乎不占额外空间,写入多少占多少。
不过要提醒一句:qcow2是“能长不能缩”。删除虚拟机里的文件后,宿主机上qcow2镜像文件并不会自动变小,因为已经占用的块不会归还给宿主机。想要真正瘦身,得用后面说的“转换+重建”技巧,或者让虚拟机支持TRIM回收未使用的块。raw在这块更麻烦,稀疏raw一旦被写脏,那些曾经写过的块也不会自动变回空洞。
2.3 快照、压缩、加密:功能差距在哪里
qcow2在这块是完胜的。内部快照可以直接用qemu-img命令创建、列出、回滚,一条命令的事,运行中的虚拟机也能配合执行。这在部署系统更新前拍个快照、出问题马上回滚的场景里堪称神器。raw想做类似的事,只能靠宿主机文件系统或LVM层做快照,操作成本高不少。
qcow2还支持压缩,压缩过的镜像在备份和传输时体积能小很多。我实际备份过一台跑着完整桌面环境的虚拟机,qcow2压缩后镜像从15GB降到9GB,省下的不只是磁盘空间,还有往备份服务器传输的时间。加密则适合存放敏感数据的虚拟机镜像,qcow2可以在创建时设置密钥。raw要实现这些功能,得分别借助压缩工具和加密文件系统,链路长且碎。
3. 实操:创建、转换、回收空间的完整命令
理论说再多,不如直接动手。这部分我把日常用的最多的命令整理出来,都是经过实际验证的。
3.1 创建raw和qcow2磁盘
创建raw磁盘且预分配:
qemu-img create -f raw disk.raw 100G这个命令会立即占用宿主机100GB的实际空间,好处是后续IO没有动态分配的开销,坏处是空间一次性吃紧。如果是临时测试,我更建议创建稀疏raw:
qemu-img create -f raw -o preallocation=off disk.raw 100G其实raw默认就是稀疏的,上面这条写出来是为了提醒你“创建时不预分配”这个动作本身是可控的。创建qcow2磁盘:
qemu-img create -f qcow2 disk.qcow2 100G创建完可以用这个命令查看实际占用:
qemu-img info disk.qcow2你会看到disk size和virtual size两栏,前者才是宿主机实际占用的空间,后者是虚拟机看到的容量。这两个数字的差距就是你省下的钱。
3.2 格式互转的正确姿势
最常用的转换方向是qcow2转raw,通常是为了提升性能:
qemu-img convert -f qcow2 -O raw source.qcow2 target.raw注意:convert默认会为raw分配全部空间,如果你希望转换后的raw仍保持稀疏,加一个参数:
qemu-img convert -p -f qcow2 -O raw -S 4k source.qcow2 target.raw这里的-S 4k意思是“小于4KB的连续零块视为空洞”,这样转换出来的文件就不会物理占用那么大。反过来raw转qcow2:
qemu-img convert -p -f raw -O qcow2 -o compat=1.1 source.raw target.qcow2compat=1.1是把qcow2版本设为现代版本,兼容性更好,也能用上更高效的元数据格式。所有转换操作前一定要先关闭虚拟机,或者至少保证文件系统处于一致状态,否则转换出来的镜像轻则文件损坏,重则直接无法启动。
注意:只要虚拟机里跑着数据库或活跃写入服务,就不要在线直接convert。我见过有人用
blockdev加drive-mirror搞在线迁移,但那套玩法对状态一致性要求极高,新手不建议碰。
3.3 把qcow2“减肥”和raw做瘦身的区别
前面说了qcow2只长不缩。当虚拟机里删了大量文件后,镜像文件依然巨大,这时候需要两步操作。第一步,在虚拟机内部执行TRIM:
fstrim -av这一步的前提是虚拟机和宿主机都开了discard支持,而且磁盘控制器最好用virtio-scsi,virtio-blk对discard的支持参差不齐。第二步,把镜像“瘦身重写”:
qemu-img convert -p -f qcow2 -O qcow2 source.qcow2 compacted.qcow2转换过程会把所有未使用的块丢掉,生成的新镜像体积肉眼可见地缩小。raw的瘦身思路类似,也是通过convert重写保留稀疏:
qemu-img convert -p -f raw -O raw -S 4k source.raw compacted.raw不过raw除非一开始就是稀疏创建,否则转换前必须先确保内部文件系统清空了不用的块,不然重写也没意义。
4. 踩坑实录与排查思路
下面几条都是我自己或者身边同事真实踩过的坑,写出来帮你提前避开。
4.1 qcow2磁盘无限膨胀
最常见的问题是:装好一台qcow2虚拟机,跑着跑着宿主机发现镜像文件越来越大,甚至连虚拟机里文件都删了还是大。原因几乎都是TRIM链路没打通。虚拟机的文件系统认为删了文件后块可以回收,但宿主机侧如果没收到discard请求,那些块就一直留在qcow2里。
排查思路按顺序来:先确认宿主机侧挂载选项里有discard或至少不阻挡TRIM;再确认虚拟机磁盘控制器和驱动支持discard;最后用lsblk -D看设备是否显示DISC-GRAN等参数不为零。如果全部OK但镜像还是膨胀,大概率是应用层面写了大量的临时数据又没有及时TRIM,定期执行一次fstrim再配合镜像重写就能解决。
4.2 转换后虚拟机起不来
raw转qcow2后启动失败,这是我被问过最多的问题。多数情况是引导配置没跟着改。虚拟机原来从raw盘引导时,引导扇区和分区表的位置是确定的;转换后不管格式怎么变,分区表内容其实没变,所以问题往往出在引导程序或内核参数上。
最典型的场景是虚拟机用传统BIOS引导,而转换时用的工具把镜像做成了GPT分区表,或者磁盘控制器的设备名从vda变成了sda,导致内核找不到根分区。解决思路是启动到救援模式,检查/boot/grub/grub.cfg或/etc/fstab里的设备路径,改成UUID引用而不是设备名。设备名跨格式转换后真的会变,UUID不会。
4.3 性能评测容易犯的错
想对比qcow2和raw性能的朋友,我建议别直接拿dd测。dd测的是顺序吞吐,对两种格式来说结论基本无差异,测不出真实差距。用fio测随机4K读写更有参考价值,命令大致长这样:
fio -filename=/dev/vdb -direct=1 -rw=randrw -bs=4k -size=1G -numjobs=4 -runtime=60 -group_reporting还有两个容易忽略的点。一是测试前确认缓存模式相同,用cache=none才算真正压到存储层;二是两台虚拟机别放在同一块宿主机硬盘上测试,否则互相干扰,测出来的数据没法看。我自己的习惯是在宿主机上用不同的LVM卷分别跑测试盘,尽量隔离。
5. 最终选型建议:别再随大流
说了这么多,到底怎么选?我给出一套自己的判断逻辑,不保证放之四海皆准,但至少能让你少走弯路。
5.1 我自己的选择逻辑
先问三个问题:虚拟机跑的业务对IOPS敏感吗?宿主机磁盘空间紧张吗?我需要快照吗?把这几个问题的答案组合一下,选择自然浮出水面。
生产环境里的数据库、缓存类服务,IOPS敏感、空间可以堆硬件、快照需求低,我基本选raw加预分配,配合定期LVM快照做备份。开发测试环境、桌面虚拟机、承载未知规模数据的业务,空间敏感、快照是刚需,统一用qcow2,省心又省空间。还有一些中间状态的虚拟机,比如存储不算紧张但希望保留回滚能力,用qcow2也完全够。
另外,宿主机磁盘类型也是个关键变量。全闪存环境下,我大概率直接无脑qcow2,因为性能差距太小,空间和快照优势却很实在;机械盘环境下,如果是IO密集应用,还是raw更稳妥,省去格式开销就等于给磁盘减负。
5.2 混合使用也完全OK
没必要把所有虚拟机都统一成一种格式。我的实际做法是:系统盘用qcow2装系统,享受快照和动态分配,数据盘用raw按需预分配,追求稳定读写性能。这种组合兼具两者的优点,尤其在数据库场景里非常合适。系统盘改动频繁,需要回滚;数据盘大文件多,只求性能稳定。一个虚拟机里把两种格式都用上,让合适的格式去干适合的事,比争论谁更好有意义得多。
最后再多说一句,格式选型这件事,不同版本的QEMU和内核表现差异不小。我见过有人在老版本上被qcow2的性能坑过,换了新版本后体验完全两样。所以做决定前,最好在自己当前版本的虚拟化平台上跑一遍fio,用数据说话,而不是凭网上三五年前的帖子做判断。毕竟虚拟化环境是你自己的,业务也是你自己的,跑出数据的那一刻,答案就明明白白摆在那里了。