「Sparse File」- 稀疏文件

认识

稀疏文件是一种聪明的存储优化技术,它通过区分文件的“逻辑视图”和“物理存储”,巧妙地避免了为空洞数据分配实际空间。它在虚拟化、数据库等需要处理大型文件的领域发挥着至关重要的作用。然而,在使用时也需要了解其特性,避免在管理、备份和传输过程中出现意外。简单来说,稀疏文件是一种计算机文件,它能够高效地存储那些大部分内容为空(通常是连续的零)的文件。

它的核心思想是:“没有必要为那些毫无意义的零值数据实际分配物理磁盘空间。”

当程序创建一个稀疏文件时,文件系统不会立即为整个文件大小分配相应的磁盘块,而是只记录文件的“逻辑大小”和其中包含实际数据的“物理大小”。

  • 逻辑大小:是文件看起来的大小,也就是你通过 `ls -l` 或文件属性看到的尺寸。
  • 物理大小:是文件实际在磁盘上占用的空间,也就是你通过 `du` 命令看到的尺寸。

对于稀疏文件,逻辑大小 >> 物理大小。

组成

技术原理

当应用程序尝试在文件中创建一个“空洞”时(例如,使用 `lseek` 然后写入),文件系统不会向后分配磁盘块,而是:

  1. 在文件的元数据中记录这个区域是“未分配”的。
  2. 当后续有读取操作访问这个“空洞”区域时,文件系统会动态地返回零值,而无需从磁盘读取任何数据。
  3. 只有当应用程序真正向“空洞”的某个部分写入非零数据时,文件系统才会为那个特定的数据块分配物理磁盘空间。

文件创建

与常规文件的创建方式不同,当创建 Sparse File 时:
1、不会将组成块的实际字节写入磁盘,
2、而是,在元数据中,记录文件大小,并将元数据写入磁盘。
3、当向文件写入数据时,才会分配实际的数据块。
4、当读取稀疏文件时,文件系统透明地将表示空块的元数据转换为在运行时填充 NULL 字节的“真实”块;

所以,能否使用 Sparse-File 取决于文件系统是否支持。

性质

优点:

  • 节省磁盘空间:这是最直接的好处。可以创建远大于可用物理存储空间的文件。
  • 提高 I/O 效率:由于不需要读写大量的零,文件操作(如创建、复制)会更快,减少了不必要的磁盘写入。
  • 节省内存:当映射稀疏文件到内存时,操作系统只会为实际使用的部分分配物理内存页。

缺点:

  • 空间报告混淆:使用 `ls -l` 看到的文件大小具有误导性,可能导致误判磁盘剩余空间。应始终使用 `du` 来检查实际占用空间。
  • 处理不当可能导致空间爆满:如果一个稀疏文件被一个不理解稀疏特性的工具处理(例如,某些简单的逐字节复制的工具),它可能会被“实体化”,即所有空洞都被真实的零填充,瞬间占满磁盘空间。
  • 碎片化:如果不断向空洞中随机写入数据,文件的实际数据块可能会在磁盘上变得非常分散,影响读取性能。
  • 备份和传输的复杂性:备份工具(如 `tar`, `rsync`)需要支持稀疏文件特性(如 `tar -S`, `rsync -S`),才能高效地传输它们,否则会在目标端创建充满 0 的实体文件。

构建

在 Linux/Unix 中

使用 `dd` 命令,这是最经典的创建方法。

dd count=0 bs=1M seek=100 of=/path/to/myfile

# 创建一个 1GB 大小,但内容全是“空洞”的文件

dd if=/dev/zero of=sparse_file.img bs=1 count=0 seek=1G

# `if=/dev/zero`:输入源,一个无限输出零的设备。
# `of=sparse_file.img`:输出文件。
# `bs=1`:块大小设为 1 字节(为了兼容性)。
# `count=0`:不从输入源复制任何块。
# `seek=1G`:在输出文件中跳过 1GB 的位置再开始写。这个“跳过”的区域就形成了空洞。

稀疏文件的实际大小

ls -lh sparse_file.img    # 查看逻辑大小,显示为 1.0G
du -h sparse_file.img     # 查看物理大小,显示为 0 或 4.0K(仅元数据)

在 Windows 中

Windows 的 NTFS 文件系统也原生支持稀疏文件,但通常通过 API(`DeviceIoControl` 与 `FSCTL_SET_SPARSE`)或在程序中设置文件属性来管理。

在资源管理器中,可以查看文件的“占用空间”和“大小”,其中“占用空间”就是物理大小。

判断稀疏文件

archlinux/Sparse file
Finding sparse files?

# 通过 find 判断文件是否为稀疏文件
# 最左边一列(%S)显示的值是(BLOCK-SIZE * st_blocks / st_size),在稀疏文件的情况下通常小于 1.0

# find /var/lib/libvirt/images f -printf "%S\t%p\n"
0.217076       ./cnicd-02.qcow2
0.152867       ./cnicd-01.qcow2
0.925253       ./ci-node-01.qcow2
1              ./develop.qcow2
0.48236        ./cluster-08.qcow2
0.100322       ./develop-235.qcow2
1              ./develop-354.qcow2

# ls shows the gray+green areas, the logical length of the file.
# du (without --apparent-size) shows only the green areas, since those are the ones that take up space.

# ls -lh  cnicd-01.qcow2
-rw------- 1 libvirt-qemu kvm 101G Mar 30 02:24 cnicd-01.qcow2

# du -h cnicd-01.qcow2
16G     cnicd-01.qcow2

# stat -c '%b*%B-%s' -- "$file"                                                 # 或使用 stat 命令

复制稀疏文件

方法还是由很多的,性能可能会有所差异,使用场景以有些不一样的地方:「What is fastest way to copy a sparse file? What method results in the smallest file?

可以使用 cp、dd、cpio、rsync、virt-sparsify 等命令,其中 virt-sparsify 是用于虚拟机迁移,当时是为了迁移虚拟机镜像文件,才有所涉猎。

这里不再深入,有需要的时候再研究,详细内容参考各个命令的手册。

应用

虚拟磁盘映像:VMware、VirtualBox、QEMU/KVM 等虚拟化软件广泛使用稀疏文件来存储虚拟机的硬盘。当你创建一个“动态分配”的 100GB 虚拟磁盘时,最初它可能只占用几 MB 空间,随着虚拟机内数据的增多而逐渐膨胀。

数据库:某些数据库使用稀疏文件来预分配大量的存储空间,以确保其有连续的存储区域,同时又不会一开始就耗尽所有磁盘空间。

日志文件:某些系统会预创建大的日志文件,但一开始并不写入数据。

科学计算:处理大型但稀疏的矩阵或数据集时非常有用。

参考

Wikipedia/Sparse file/https://en.wikipedia.org/wiki/Sparse_file
What is fastest way to copy a sparse file? What method results in the smallest file?
Sparse Files – GeeksforGeeks
How to find all the sparse files in Linux
DeepSeek / 介绍稀疏文件 Sparse File