ZFS 数据集管理
Table of Contents
创建和销毁数据集
与传统磁盘和卷管理器不同,ZFS 不会预先分配空间 传统文件系统在分区并分配空间后,若不添加新磁盘,便无法创建新的文件系统
ZFS 允许随时创建新的文件系统
- 每个 数据集 都具备压缩、去重、缓存和配额等特性,此外还有只读、大小写敏感、网络文件共享和挂载点等实用属性
- 数据集可以嵌套,子数据集会继承父数据集的属性
- 每个数据集都可以委托管理、复制、创建快照、纳入 jail,也可以直接销毁
为每种文件类型或文件集合创建单独的数据集是一种推荐做法 然而,数据集过多亦存在不足:zfs list 等命令会变慢,挂载数百乃至数千个数据集也会拖慢 FreeBSD 的启动速度
查看目前的数据集:
$ zfs list NAME USED AVAIL REFER MOUNTPOINT zroot 916M 49.0G 96K /zroot zroot/ROOT 886M 49.0G 96K none zroot/ROOT/default 886M 49.0G 886M / zroot/home 27.8M 49.0G 96K /home zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla zroot/tmp 128K 49.0G 128K /tmp zroot/usr 288K 49.0G 96K /usr zroot/usr/ports 96K 49.0G 96K /usr/ports zroot/usr/src 96K 49.0G 96K /usr/src zroot/var 636K 49.0G 96K /var zroot/var/audit 96K 49.0G 96K /var/audit zroot/var/crash 96K 49.0G 96K /var/crash zroot/var/log 156K 49.0G 156K /var/log zroot/var/mail 96K 49.0G 96K /var/mail zroot/var/tmp 96K 49.0G 96K /var/tmp
本节示例中出现的用户名 ykla 及路径 /home/ykla 均为示例,请根据自身环境替换为实际用户名和主目录
创建新的数据集并启用 LZ4 压缩:
$ zfs create -o compression=lz4 zroot/usr/mydataset $ zfs list NAME USED AVAIL REFER MOUNTPOINT zroot 916M 49.0G 96K /zroot zroot/ROOT 886M 49.0G 96K none zroot/ROOT/default 886M 49.0G 886M / zroot/home 27.8M 49.0G 96K /home zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla zroot/tmp 128K 49.0G 128K /tmp zroot/usr 384K 49.0G 96K /usr zroot/usr/mydataset 96K 49.0G 96K /usr/mydataset # 注意此行 zroot/usr/ports 96K 49.0G 96K /usr/ports zroot/usr/src 96K 49.0G 96K /usr/src zroot/var 636K 49.0G 96K /var zroot/var/audit 96K 49.0G 96K /var/audit zroot/var/crash 96K 49.0G 96K /var/crash zroot/var/log 156K 49.0G 156K /var/log zroot/var/mail 96K 49.0G 96K /var/mail zroot/var/tmp 96K 49.0G 96K /var/tmp
销毁创建的数据集:
$ zfs destroy zroot/usr/mydataset $ zfs list NAME USED AVAIL REFER MOUNTPOINT zroot 916M 49.0G 96K /zroot zroot/ROOT 886M 49.0G 96K none zroot/ROOT/default 886M 49.0G 886M / zroot/home 27.8M 49.0G 96K /home zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla zroot/tmp 128K 49.0G 128K /tmp zroot/usr 288K 49.0G 96K /usr zroot/usr/ports 96K 49.0G 96K /usr/ports zroot/usr/src 96K 49.0G 96K /usr/src zroot/var 636K 49.0G 96K /var zroot/var/audit 96K 49.0G 96K /var/audit zroot/var/crash 96K 49.0G 96K /var/crash zroot/var/log 156K 49.0G 156K /var/log zroot/var/mail 96K 49.0G 96K /var/mail zroot/var/tmp 96K 49.0G 96K /var/tmp
由于此操作不涉及扫描文件和更新相应的元数据,销毁数据集比删除数据集中的文件要快得多
在现代版本的 ZFS 中,zfs destroy 是异步的,释放的空间可能几分钟后才会在池中体现。使用 zpool get freeing 存储池 查看 freeing 属性:
NAME PROPERTY VALUE SOURCE zroot freeing 0 -
该属性会显示哪些数据集正在后台释放其块。如果有子数据集(例如快照或其他数据集),则无法直接销毁父数据集
- 要销毁数据集及其所有子数据集,可使用 -r 递归销毁
- 使用 -n -v 可以列出此操作将销毁的数据集和快照,但不会实际销毁任何数据
- 销毁快照时,回收的空间也会一并显示
zfs destroy 将永久删除数据集及其所有数据,且无法撤销 请务必确认操作对象正确无误,必要时可先用 zfs destroy -n -v 预览待销毁的数据集和快照
创建和销毁卷
卷是一种特殊的数据集类型。它不作为文件系统挂载,而是以 块设备 的形式暴露于 /dev/zvol/poolname/dataset 路径下
因此,卷可用于其他文件系统、充当虚拟机磁盘,或通过 iSCSI 及 HAST 等协议提供给网络上的其他主机
卷可以格式化并使用任何文件系统,也可以不格式化而直接存储原始数据。对用户而言,卷与普通磁盘无异。在这些 zvol 上放置普通文件系统,可以获得普通磁盘或文件系统所不具备的功能
例如,对一个 250 MB 的卷启用压缩属性,即可创建一个压缩的 FAT 文件系统
创建 3 GB 的 ZFS 卷并启用压缩:
$ zfs create -V 3G -o compression=on zroot/fat32
如果设置的 FAT32 容量过低,由于文件簇不足,将无法正常格式化
确认卷已创建并查看其空间占用:
$ zfs list zroot/fat32 NAME USED AVAIL REFER MOUNTPOINT zroot/fat32 3.05G 49.0G 56K -
卷以块设备形式出现在 dev/zvol 路径下:
$ ls -al /dev/zvol/zroot/fat32 crw-r----- 1 root operator 0x75 Apr 16 12:21 /dev/zvol/zroot/fat32
将卷格式化为 FAT32 文件系统:
$ newfs_msdos -F32 /dev/zvol/zroot/fat32 newfs_msdos: cannot get number of sectors per track: Operation not supported newfs_msdos: cannot get number of heads: Operation not supported /dev/zvol/zroot/fat32: 6288320 sectors in 98255 FAT32 clusters (32768 bytes/cluster) BytesPerSec=512 SecPerClust=64 ResSectors=64 FATs=2 Media=0xf0 SecPerTrack=63 Heads=255 HiddenSecs=0 HugeSectors=6291456 FATsecs=768 RootCluster=2 FSInfo=1 Backup=2
newfs_msdos 将格式化指定的 ZFS 卷,卷上已有数据将永久丢失。请确认设备路径 /dev/zvol/ 正确无误,避免误格式化其他设备
挂载卷并确认挂载成功:
$ mount -t msdosfs /dev/zvol/zroot/fat32 /mnt $ mount | grep fat32 /dev/zvol/zroot/fat32 on /mnt (msdosfs, local)
复制一些文件以测试:
$ cp /home/ykla/Philosophische-untersuchungen.pdf /mnt/ $ df -h /mnt | grep fat32 /dev/zvol/zroot/fat32 3.0G 36M 3.0G 1% /mnt
销毁卷的过程与销毁常规文件系统数据集类似:
$ zfs destroy zroot/fat32
该操作几乎即时完成,但空闲空间可能需要几分钟才能在后台回收
重命名数据集
要更改数据集的名称,可使用 zfs rename 命令。要更改数据集的父级,同样使用该命令
- 将数据集重命名至不同的父级下,会改变其从父级继承的属性值
- 重命名数据集时,会卸载该数据集并将其挂载到新位置(该位置从新父级继承)
- 要阻止此行为,可使用 -u 选项
将数据集重命名并移动到不同的父级:
创建用于演示的数据集并确认其位置:
$ zfs create zroot/usr/mydataset $ zfs list NAME USED AVAIL REFER MOUNTPOINT zroot 3.94G 46.0G 96K /zroot zroot/ROOT 886M 46.0G 96K none zroot/ROOT/default 886M 46.0G 886M / zroot/fat32 3.05G 49.0G 31.0M - zroot/home 27.8M 46.0G 96K /home zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla zroot/tmp 128K 46.0G 128K /tmp zroot/usr 384K 46.0G 96K /usr zroot/usr/mydataset 96K 46.0G 96K /usr/mydataset # 注意此行 zroot/usr/ports 96K 46.0G 96K /usr/ports zroot/usr/src 96K 46.0G 96K /usr/src zroot/var 636K 46.0G 96K /var zroot/var/audit 96K 46.0G 96K /var/audit zroot/var/crash 96K 46.0G 96K /var/crash zroot/var/log 156K 46.0G 156K /var/log zroot/var/mail 96K 46.0G 96K /var/mail zroot/var/tmp 96K 46.0G 96K /var/tmp
给数据集创建快照,演示重命名后快照仍会保留:
$ zfs snapshot zroot/usr/mydataset@01 $ zfs list -t snapshot NAME USED AVAIL REFER MOUNTPOINT zroot/usr/mydataset@01 0B - 96K -
执行重命名,将数据集从 zroot/usr 移至 zroot/var 下:
$ zfs rename zroot/usr/mydataset zroot/var/newname $ zfs list NAME USED AVAIL REFER MOUNTPOINT zroot 3.94G 46.0G 96K /zroot zroot/ROOT 886M 46.0G 96K none zroot/ROOT/default 886M 46.0G 886M / zroot/fat32 3.05G 49.0G 31.0M - zroot/home 27.8M 46.0G 96K /home zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla zroot/tmp 128K 46.0G 128K /tmp zroot/usr 288K 46.0G 96K /usr zroot/usr/ports 96K 46.0G 96K /usr/ports zroot/usr/src 96K 46.0G 96K /usr/src zroot/var 732K 46.0G 96K /var zroot/var/audit 96K 46.0G 96K /var/audit zroot/var/crash 96K 46.0G 96K /var/crash zroot/var/log 156K 46.0G 156K /var/log zroot/var/mail 96K 46.0G 96K /var/mail zroot/var/newname 96K 46.0G 96K /var/newname # 注意此行 zroot/var/tmp 96K 46.0G 96K /var/tmp
快照无法更改父级。再创建一个快照验证:
$ zfs snapshot zroot/var/newname@02 $ zfs list -t snapshot NAME USED AVAIL REFER MOUNTPOINT zroot/var/newname@01 0B - 96K - zroot/var/newname@02 0B - 96K -
重命名快照也使用相同的命令:
$ zfs rename zroot/var/newname@02 zroot/var/newname@03 $ zfs list -t snapshot NAME USED AVAIL REFER MOUNTPOINT zroot/var/newname@01 0B - 96K - zroot/var/newname@03 0B - 96K -
要递归重命名快照,可使用 -r 选项,这将重命名所有子数据集中具有相同名称的快照。 -r 同样适用于递归重命名数据集及其所有子数据集
设置数据集属性
每个 ZFS 数据集都有一组控制其行为的属性。大多数属性默认从父数据集继承,但可以单独覆盖
- 使用 zfs set 命令设置数据集属性,语法为 <property=value dataset>
- 大多数属性的有效值范围有限,_zfs get_ 会列出每种属性及其有效值
- 通过 zfs get all <数据集> 可查看数据集当前的所有属性(包括继承值和本地设置值),方便问题诊断与状态确认
- 使用 zfs inherit 可以将大多数属性恢复为继承值
- 还可以定义用户自定义属性,这些属性成为数据集配置的一部分,可提供关于数据集或其内容的附加信息
- 为区分自定义属性与 ZFS 内置属性,可使用冒号 : 创建自定义命名空间
设置自定义属性并查看:
$ zfs set custom:costcenter=1234 zroot/var/newname
$ zfs get custom:costcenter zroot/var/newname
NAME PROPERTY VALUE SOURCE
zroot/var/newname custom:costcenter 1234 local
要删除自定义属性,使用 zfs inherit 并加上 -r 选项
如果自定义属性在任何父级数据集中未定义,此选项将删除它(但池的历史记录仍然会记录该更改)
删除自定义属性并验证:
$ zfs inherit -r custom:costcenter zroot/var/newname
$ zfs get custom:costcenter zroot/var/newname
NAME PROPERTY VALUE SOURCE
zroot/var/newname custom:costcenter - -
$ zfs get all zroot/var/newname | grep custom:costcenter
#
获取和设置共享属性
两个常用且实用的数据集属性是 NFS 和 SMB 共享选项。设置这些属性可以定义 ZFS 是否通过网络共享数据集以及如何共享
目前,FreeBSD 仅支持设置 NFS 共享
要获取共享的当前状态,可输入:
$ zfs get sharenfs zroot/home NAME PROPERTY VALUE SOURCE zroot/home sharenfs off default
要启用数据集的共享,可以输入:
$ zfs set sharenfs=on zroot/home
$ zfs get sharenfs zroot/home
NAME PROPERTY VALUE SOURCE
zroot/home sharenfs on local
可以为通过 NFS 共享的数据集设置其他选项,如 -alldirs、-maproot 和 -network。要设置共享选项,可以输入:
$ zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" zroot/home $ zfs get sharenfs zroot/home NAME PROPERTY VALUE SOURCE zroot/home sharenfs -alldirs,-maproot=root,-network=192.168.1.0/24 local
管理快照
ZFS 快照采用 写时复制 COW 机制,创建过程瞬时完成且不占用额外磁盘空间
- 快照创建后,原文件系统中的数据块发生修改时,ZFS 将新数据写入新位置,旧数据块原地保留,从而捕获文件系统在快照创建时刻的时间点状态
- 快照提供了数据集的只读、时间点副本
- 快照作用于整个数据集而非单个文件,在创建时不额外占用空间,随其引用的块发生变化而消耗空间
快照的典型用途是,在执行软件安装或系统升级等高风险操作之前,快速备份当前文件系统状态。如果操作失败,可回滚到快照恢复系统状态;如果升级成功,则删除快照释放空间。快照回滚速度很快,几乎不会造成停机。快照不能替代完整的池备份,但在特定时刻保存数据集副本方面极为高效
FreeBSD 中文社区(CFC)提供了 ZFS 脚本,可用于查看、创建、删除和恢复 ZFS 快照(ZFS snapshot) ZFS 脚本项目地址。该脚本已部署至 https://docs.bsdcn.org/zfs.sh,可在 FreeBSD 系统上直接使用 fetch 命令下载
创建快照
在默认情况下,使用 Auto ZFS 布局创建的分区结构如下:
$ zfs list # 列出所有 ZFS 文件系统及其属性 NAME USED AVAIL REFER MOUNTPOINT zroot 3.94G 46.0G 96K /zroot zroot/ROOT 886M 46.0G 96K none zroot/ROOT/default 886M 46.0G 886M / zroot/fat32 3.05G 49.0G 31.0M - zroot/home 27.8M 46.0G 96K /home zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla zroot/tmp 128K 46.0G 128K /tmp zroot/usr 288K 46.0G 96K /usr zroot/usr/ports 96K 46.0G 96K /usr/ports zroot/usr/src 96K 46.0G 96K /usr/src zroot/var 732K 46.0G 96K /var zroot/var/audit 96K 46.0G 96K /var/audit zroot/var/crash 96K 46.0G 96K /var/crash zroot/var/log 156K 46.0G 156K /var/log zroot/var/mail 96K 46.0G 96K /var/mail zroot/var/newname 96K 46.0G 96K /var/newname zroot/var/tmp 96K 46.0G 96K /var/tmp
要创建快照,可以使用 zfs snapshot <数据集>@<快照名> 命令。添加 -r 选项可以 递归 创建快照,在所有子数据集上使用相同的名称。创建整个池的递归快照:
$ zfs snapshot -r zroot@test # 为 zroot 池及其所有子文件系统递归创建快照 test
快照不会在正常的 zfs list 操作中显示。要列出快照,必须在 zfs list 命令后加上 -t snapshot 选项。 -t all 会显示文件系统和快照
$ zfs list -t snap # 列出所有 ZFS 快照 NAME USED AVAIL REFER MOUNTPOINT zroot@test 0B - 96K - zroot/ROOT@test 0B - 96K - zroot/ROOT/default@test 0B - 886M - zroot/fat32@test 0B - 31.0M - zroot/home@test 0B - 96K - zroot/home/ykla@test 0B - 27.7M - zroot/tmp@test 0B - 128K - zroot/usr@test 0B - 96K - zroot/usr/ports@test 0B - 96K - zroot/usr/src@test 0B - 96K - zroot/var@test 0B - 96K - zroot/var/audit@test 0B - 96K - zroot/var/crash@test 0B - 96K - zroot/var/log@test 0B - 156K - zroot/var/mail@test 0B - 96K - zroot/var/newname@01 0B - 96K - zroot/var/newname@03 0B - 96K - zroot/var/newname@test 0B - 96K - zroot/var/tmp@test 0B - 96K - # 注意此行
在命令中,snapshot 可以缩写为 snap,其他命令也有对应的缩写形式,可自行查阅文档
快照不会直接挂载,因此在 MOUNTPOINT 列中不会显示路径。由于快照在创建后是只读的,因此 ZFS 不会在 AVAIL 列中显示可用空间。可以通过以下命令比较快照与原数据集:
$ zfs list -rt all zroot/home NAME USED AVAIL REFER MOUNTPOINT zroot/home 27.8M 45.9G 96K /home zroot/home@test 0B - 96K - zroot/home/ykla 27.7M 45.9G 27.7M /home/ykla zroot/home/ykla@test 0B - 27.7M -
同时显示数据集和快照可以揭示快照如何以写时复制(COW)方式工作 它们只保存更改的部分(delta),而不是重新保存整个文件系统的内容。这意味着快照在发生变化时占用的空间极小
通过将文件复制到数据集后再创建第二个快照,可以更清楚地观察空间占用情况:
$ cp /COPYRIGHT /var/tmp $ zfs snapshot zroot/var/tmp@test2
查看创建第二个快照后各快照的空间占用变化:
$ zfs list -rt all zroot/var/tmp NAME USED AVAIL REFER MOUNTPOINT zroot/var/tmp 164K 45.9G 100K /var/tmp zroot/var/tmp@test 64K - 96K - zroot/var/tmp@test2 0B - 100K -
第二个快照只包含复制操作后数据集的更改,因此极大节省了空间
快照 zroot/var/tmp@test 的大小在 USED 列中也发生了变化,这反映的是它与之后创建的快照之间的差异
销毁快照
销毁快照时,可以使用 -r 参数递归删除:
$ zfs destroy -r zroot@test # 递归删除 zroot 池及其子文件系统的 test 快照 $ zfs list -t snap # 列出所有 ZFS 快照 no datasets available
快照保持
有时需要防止关键快照被误删 例如在备份流程尚未完成之前,或因法规要求必须保留的特定时间点副本
ZFS 的 保持 hold 机制可对快照施加命名的轻量级引用,使其在保持解除前无法被 zfs destroy 删除. 为快照添加保持标签:
$ zfs hold keeptest zroot/var/tmp@test
keeptest 只是便于识别的标签,可以修改为其他名称
查看快照上的所有保持:
$ zfs holds zroot/var/tmp@test NAME TAG TIMESTAMP zroot/var/tmp@test keeptest Wed May 13 13:18 2026
尝试销毁被保持的快照会返回错误:
$ zfs destroy zroot/var/tmp@test cannot destroy snapshot zroot/var/tmp@test: it's being held. Run 'zfs holds -r zroot/var/tmp@test' to see holders.
使用 -r 选项可递归地对所有子数据集的同名快照施加保持:
$ zfs hold -r keepall zroot@test $ zfs holds -r zroot@test NAME TAG TIMESTAMP zroot@test keepall Wed May 13 13:18 2026 zroot/ROOT@test keepall Wed May 13 13:18 2026 zroot/ROOT/default@test keepall Wed May 13 13:18 2026 zroot/home@test keepall Wed May 13 13:18 2026 zroot/home/ykla@test keepall Wed May 13 13:18 2026 zroot/tmp@test keepall Wed May 13 13:18 2026 zroot/usr@test keepall Wed May 13 13:18 2026 zroot/usr/ports@test keepall Wed May 13 13:18 2026 zroot/usr/src@test keepall Wed May 13 13:18 2026 zroot/var@test keepall Wed May 13 13:18 2026 zroot/var/audit@test keepall Wed May 13 13:18 2026 zroot/var/crash@test keepall Wed May 13 13:18 2026 zroot/var/log@test keepall Wed May 13 13:18 2026 zroot/var/mail@test keepall Wed May 13 13:18 2026 zroot/var/tmp@test keepall Wed May 13 13:18 2026 zroot/var/tmp@test keeptest Wed May 13 13:18 2026
释放保持标签后,快照即可正常销毁:
$ zfs release keeptest zroot/var/tmp@test $ zfs release keepall zroot/var/tmp@test $ zfs destroy zroot/var/tmp@test
zfs send 的 -h 选项可在发送流中包含保持标签。这对灾难恢复环境中保持备份完整性特别有用
$ zfs send -h zroot/var/tmp@test | zfs receive backup/var/tmp
接收端使用 zfs receive 接收该流时,快照的保持将在接收方自动重建
比较快照
ZFS 提供了内建命令用于比较两个快照之间内容的差异
对于长期保存大量快照的场景,此功能极为实用,用户可借此查看文件系统随时间推移的变化 例如,zfs diff 可帮助用户找到最近的快照,检查其中是否仍包含误删除的文件
比较前面章节中创建的两个快照,得到以下输出:
$ zfs diff zroot/var/tmp@test + /var/tmp/COPYRIGHT M /var/tmp/
该命令列出了指定快照(此处为 zroot/var/tmp@test)与当前文件系统之间的变化
第一列显示更改类型:
| 命令 | 功能 |
| + | 添加路径或文件 |
| - | 删除路径或文件 |
| M | 修改路径或文件 |
| R | 重命名路径或文件 |
将输出与上述符号对照可知,ZFS 在创建 zroot/var/tmp@test 快照之后添加了 COPYRIGHT 文件 这也导致挂载在 /var/tmp 的父目录发生了修改
通过完整的数据集名称和两个快照名称来比较两个快照
$ cp /var/tmp/COPYRIGHT /var/tmp/COPYRIGHT.copy $ zfs snapshot zroot/var/tmp@diff $ zfs diff zroot/var/tmp@test zroot/var/tmp@diff + /var/tmp/COPYRIGHT M /var/tmp/ + /var/tmp/COPYRIGHT.copy $ zfs diff zroot/var/tmp@test zroot/var/tmp@test2 + /var/tmp/COPYRIGHT M /var/tmp/
两个快照的比较在借助 ZFS 复制功能将数据集传输到不同主机备份时非常有用 备份管理员可以比较从发送主机接收到的两个快照,并确定数据集中的实际变化
快照回滚
只要存在至少一个快照,随时都可以回滚到该快照
回滚最常见的场景是:当前数据集的状态已失效,或者更适合使用旧版本 例如,本地开发测试出错、系统更新失败导致功能受损,或者需要恢复已删除的文件或目录, 这些情况都很常见
要回滚到快照,可使用 zfs rollback <snapshotname> 命令
- 如果更改量较大,操作可能耗时较长
- 在此期间,数据集始终保持一致状态,如同符合 ACID 原则的数据库执行回滚一般
- 整个过程在数据集在线且无需停机的情况下完成
回滚之后,数据集的状态将恢复到快照创建时的状态。回滚到快照会丢弃该数据集中所有不属于该快照的数据
如果在回滚到更早快照之前先为当前状态创建快照,则之后需要某些数据时可以方便地回滚 这样,用户可以在快照之间来回切换,而不会丢失仍有价值的数据
快照还原测试
可以通过增删文件来验证快照的有效性 在测试环境中,如果事先创建了快照,即使执行 rm -rf /* 命令也可以顺利恢复 如果系统使用 UEFI,则需要根据其他章节的说明自行恢复 EFI 引导
假设在之前示例中,因误执行 rm 命令删除了超出预期的数据,需要回滚到快照:
首先查看当前可用的快照:
$ zfs list -rt all zroot/var/tmp NAME USED AVAIL REFER MOUNTPOINT zroot/var/tmp 232K 45.9G 112K /var/tmp zroot/var/tmp@test 64K - 96K - zroot/var/tmp@test2 56K - 100K - zroot/var/tmp@diff 0B - 112K -
查看当前目录下的文件:
$ ls /var/tmp COPYRIGHT COPYRIGHT.copy vi.recover
模拟误删除操作,移除 COPYRIGHT 相关文件:
$ rm /var/tmp/COPYRIGHT* $ ls /var/tmp vi.recover
此时,用户发现误删了多余的文件,希望将其恢复 ZFS 提供了一种简便的恢复方式:只要定期为重要数据创建快照即可 回滚到上一个快照后,便可找回丢失的文件,并从该点重新开始
执行回滚操作,将数据集恢复到 diff 快照的状态:
$ zfs rollback zroot/var/tmp@diff $ ls -al /var/tmp total 19 drwxrwxrwt 3 root wheel 5 Apr 16 13:01 . drwxr-xr-x 25 root wheel 25 Apr 16 12:33 .. -r--r--r-- 1 root wheel 6070 Apr 16 12:56 COPYRIGHT -r--r--r-- 1 root wheel 6070 Apr 16 13:01 COPYRIGHT.copy drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover
回滚操作将数据集恢复到最后一个快照的状态
确认回滚后快照列表未变:
$ zfs list -rt snapshot zroot/var/tmp NAME USED AVAIL REFER MOUNTPOINT zroot/var/tmp@test 64K - 96K - zroot/var/tmp@test2 56K - 100K - zroot/var/tmp@diff 0B - 112K -
也可以回滚到更早的快照,即使之后还有其他快照存在。尝试这样做时,ZFS 会显示以下警告:
$ zfs rollback zroot/var/tmp@test cannot rollback to 'zroot/var/tmp@test': more recent snapshots or bookmarks exist use '-r' to force deletion of the following snapshots and bookmarks: zroot/var/tmp@test2 zroot/var/tmp@diff
此警告表明,在目标快照与当前数据集状态之间存在其他快照。要完成回滚,必须删除这些快照 与虚拟机快照不同,默认情况下,zfs rollback 命令只能回滚到最新快照 由于快照是只读的,除非用户使用 -r 选项确认这是所需操作并销毁比目标快照更新的所有快照 否则 ZFS 无法跟踪数据集跨不同状态的所有更改
使用 -r 选项后,ZFS 将销毁比目标快照更新的所有快照,从而允许回滚到非最新的快照。如果这确实是预期操作,且用户理解删除所有中间快照的后果,可执行以下命令:
$ zfs rollback -r zroot/var/tmp@test $ zfs list -rt snapshot zroot/var/tmp NAME USED AVAIL REFER MOUNTPOINT zroot/var/tmp@test 0B - 96K - $ ls -al /var/tmp total 10 drwxrwxrwt 3 root wheel 3 Apr 16 19:49 . drwxr-xr-x 25 root wheel 25 Apr 16 12:33 .. drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover
从 zfs list -t snapshot 的输出可以确认,在执行 zfs rollback -r 后,中间快照已删除
ZFS 不支持一次性递归回滚所有子数据集,需要对每个子文件系统单独执行回滚操作
$ zfs rollback -r zroot@test # 回滚 zroot 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/ROOT@test # 回滚 zroot/ROOT 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/ROOT/default@test # 回滚 zroot/ROOT/default 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/tmp@test # 回滚 zroot/tmp 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/usr@test # 回滚 zroot/usr 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/home@test # 回滚 zroot/home 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/usr/ports@test # 回滚 zroot/usr/ports 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/var@test # 回滚 zroot/var 到 test 快照并销毁更新的快照 $ zfs rollback -r zroot/var/log@test # 回滚 zroot/var/log 到 test 快照并销毁更新的快照
从快照中恢复单个文件
快照存储在父数据集下的隐藏目录 .zfs/snapshots/快照名称 中
默认情况下,即使执行 ls -a 命令,这些目录也不会显示 尽管不可见,但仍可像普通目录一样访问
属性 snapdir 控制着这些隐藏目录是否出现在目录列表中。将该属性设为 visible 后,它们便会显示在 ls 及其他涉及目录内容的命令输出中。查看 snapdir 属性的当前设置,再将其切换为 visible:
$ zfs get snapdir zroot/var/tmp NAME PROPERTY VALUE SOURCE zroot/var/tmp snapdir hidden default $ ls -a /var/tmp . .. vi.recover $ zfs set snapdir=visible zroot/var/tmp $ ls -al /var/tmp total 10 drwxrwxrwt 4 root wheel 3 Apr 16 19:49 . drwxr-xr-x 25 root wheel 25 Apr 16 12:33 .. dr-xr-xr-x+ 3 root wheel 3 Apr 16 19:46 .zfs # 注意此行 drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover
通过将快照中的文件复制回父数据集,可将单个文件恢复到先前状态。.zfs/snapshot 下的目录结构包含与之前创建的快照同名的目录,便于识别
以下示例展示了如何从隐藏的 .zfs 目录中恢复文件 将文件从包含目标文件的快照中复制回来
删除文件后,通过 .zfs/snapshot 目录找到并恢复:
$ rm -rf /var/tmp/vi.recover $ ls -a /var/tmp . .. .zfs $ ls /var/tmp/.zfs/snapshot test $ ls /var/tmp/.zfs/snapshot/test vi.recover $ cp -r /var/tmp/.zfs/snapshot/test/vi.recover /var/tmp $ ls /var/tmp/ .zfs vi.recover
即使将属性 snapdir 还原为默认属性 hidden,执行 ls .zfs/snapshot 仍会列出该目录的内容 管理员可自行决定是否显示这些目录,该设置作用于每个数据集
将 snapdir 恢复为 hidden 后,仍可通过路径直接访问快照:
$ zfs set snapdir=hidden zroot/var/tmp
$ ls /var/tmp/.zfs/snapshot/test
vi.recover
从隐藏的 .zfs/snapshot 目录复制文件或目录非常简便。反过来尝试将文件复制到快照目录时,会出现以下错误:尝试向快照目录写入文件,验证快照的只读特性:
$ cp /COPYRIGHT /var/tmp/.zfs/snapshot/test/ cp: /var/tmp/.zfs/snapshot/test/COPYRIGHT: Read-only file system
此错误提醒用户:快照是只读的,创建后不可更改。无法向快照目录复制文件或从中删除文件 因为这会改变快照所代表的数据集状态
快照随父文件系统自快照创建以来所发生的变化而消耗空间。快照的 written 属性可跟踪该快照占用的空间
要销毁快照并回收空间,可使用 zfs destroy <数据集>@<快照> 命令
- 加上 -r 选项可递归删除父数据集下所有同名快照
- 使用 -n -v 选项时,命令会列出将要删除的快照及其预计回收的空间,而不执行实际销毁
克隆
克隆 是快照的 可写副本 ,可写、可挂载,且拥有自己的属性
- 使用 zfs clone 创建克隆之后,原始快照便无法销毁
- 要反转克隆与快照之间的父子关系,可使用 zfs promote
- 提升克隆后,快照将成为克隆的子项,原来父子关系下的空间占用计算方式也会相应改变
- 克隆可以挂载到 ZFS 文件系统层次结构中的任意位置
创建克隆
下面是展示克隆功能的示例数据集:
$ zfs list -rt all zroot/home/ykla NAME USED AVAIL REFER MOUNTPOINT zroot/home/ykla 27.7M 45.9G 27.7M /home/ykla zroot/home/ykla@test 0B - 27.7M -
克隆的典型用途是保留快照的同时,对特定数据集做实验,以便在出错时可以恢复
由于快照无法更改,可为其创建一个可读写的克隆 实验取得理想结果后,可将克隆提升为数据集,并删除初始的文件系统 删除原始数据集并非必要,因为克隆与数据集可以共存而不会引发问题
从快照创建克隆,并验证克隆与原数据集内容一致:
$ zfs clone zroot/home/ykla@test zroot/home/ykla2 $ ls -al /home/ykla* /home/ykla: total 28222 drwxr-xr-x 2 ykla ykla 11 Apr 16 11:55 . drwxr-xr-x 4 root wheel 4 Apr 16 13:30 .. -rw-r--r-- 1 ykla ykla 950 Apr 16 19:50 .cshrc -rw-r--r-- 1 ykla ykla 311 Apr 16 19:50 .login -rw-r--r-- 1 ykla ykla 79 Apr 16 19:50 .login_conf -rw------- 1 ykla ykla 289 Apr 16 19:50 .mail_aliases -rw-r--r-- 1 ykla ykla 255 Apr 16 19:50 .mailrc -rw-r--r-- 1 ykla ykla 966 Apr 16 19:50 .profile -rw-r--r-- 1 ykla ykla 1042 Apr 16 19:50 .shrc -rw-r--r-- 1 ykla ykla 37635246 May 10 13:23 Philosophische-untersuchungen.pdf -rw-r--r-- 1 root ykla 118 Apr 16 11:58 checksum.txt /home/ykla2: total 28222 drwxr-xr-x 2 ykla ykla 11 Apr 16 11:55 . drwxr-xr-x 4 root wheel 4 Apr 16 13:30 .. -rw-r--r-- 1 ykla ykla 950 Apr 16 19:50 .cshrc -rw-r--r-- 1 ykla ykla 311 Apr 16 19:50 .login -rw-r--r-- 1 ykla ykla 79 Apr 16 19:50 .login_conf -rw------- 1 ykla ykla 289 Apr 16 19:50 .mail_aliases -rw-r--r-- 1 ykla ykla 255 Apr 16 19:50 .mailrc -rw-r--r-- 1 ykla ykla 966 Apr 16 19:50 .profile -rw-r--r-- 1 ykla ykla 1042 Apr 16 19:50 .shrc -rw-r--r-- 1 ykla ykla 37635246 May 10 13:23 Philosophische-untersuchungen.pdf -rw-r--r-- 1 root ykla 118 Apr 16 11:58 checksum.txt
查看克隆与原数据集的磁盘占用,二者共享同一份数据块:
$ df -h /home* Filesystem Size Used Avail Capacity Mounted on zroot/home 46G 96K 46G 0% /home $ df -h /home/ykla* Filesystem Size Used Avail Capacity Mounted on zroot/home/ykla 46G 28M 46G 0% /home/ykla zroot/home/ykla2 46G 28M 46G 0% /home/ykla2
提升克隆
克隆创建时,它是快照创建时刻数据集的完整副本 此后,克隆可独立于原始数据集更改 两者之间由快照关联,ZFS 在 origin 属性中记录这一关联
使用 zfs promote 提升克隆后,克隆将变为独立的数据集,origin 属性的值已清除,克隆与快照之间的关联也随之断开。以下示例展示了这一过程:
提升前,查看克隆的 origin 属性,确认源自快照:
$ zfs get origin zroot/home/ykla2 NAME PROPERTY VALUE SOURCE zroot/home/ykla2 origin zroot/home/ykla@test -
执行提升操作:
$ zfs promote zroot/home/ykla2
提升后 origin 属性已清除,克隆成为独立数据集:
$ zfs get origin zroot/home/ykla2 NAME PROPERTY VALUE SOURCE zroot/home/ykla2 origin - -
在克隆中添加新文件:
$ cp /COPYRIGHT /home/ykla2
将 COPYRIGHT 复制到提升后的克隆中,旧目录便已过时 此时可以用提升后的克隆替换它
删除原始数据集:
$ zfs destroy -f zroot/home/ykla
将克隆重命名为原数据集名称,完成替换:
$ zfs rename zroot/home/ykla2 zroot/home/ykla $ ls /home/ykla .cshrc .profile .login .shrc .login_conf COPYRIGHT .mail_aliases Philosophische-untersuchungen.pdf .mailrc checksum.txt $ df -h /home/ykla Filesystem Size Used Avail Capacity Mounted on zroot/home/ykla 46G 28M 46G 0% /home/ykla
至此,从快照克隆而来的数据集已成为一个普通数据集 它包含原始快照的所有数据以及新增的文件(如 COPYRIGHT)
克隆为 ZFS 用户提供了多种场景下的实用功能
例如,可以为 jail 提供包含不同应用集的快照,用户克隆这些快照后可按需添加自己的应用程序 对更改满意后,可将克隆提升为完整数据集,并交付给最终用户,最终用户可将其当作普通数据集使用 这大大节省了提供 jail 所需的时间和管理开销
复制
将数据仅存储在单个池的单一位置中,会使数据面临盗窃、自然灾害或人为破坏等风险 因此,定期备份整个池至关重要
ZFS 提供了内置的序列化功能,可将数据的流表示发送到标准输出。借助此功能,既可以将数据存储到连接至本地系统的另一个池中,也可以通过网络将数据发送到另一台系统。快照是这种复制的基础。用于复制数据的命令是 zfs send 和 zfs receive
创建所需测试存储池:
$ mdconfig -a -t swap -s 1G md0 $ mdconfig -a -t swap -s 1G md1 $ zpool create mypool md0 $ zpool create backup md1 $ zpool list backup mypool NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT backup 960M 372K 960M - - 7% 0% 1.00x ONLINE - mypool 960M 372K 960M - - 7% 0% 1.00x ONLINE -
复制若干 非重要 文件以便于测试:
$ cp /home/ykla/bsdbook.pdf /mypool/
以下示例展示了如何使用这两个池完成 ZFS 复制:
- 存储池 mypool 是主池,用于读写常规数据
- 第二个池 backup 作为备用,以防主池不可用
请注意,ZFS 不会自动执行故障转移,需要管理员在必要时手动操作 利用快照可以获取一致的文件系统版本用以复制还原
为 mypool 创建快照后,即可通过复制快照将其传输到 backup 池。此操作不包含自上次快照以来的更改
$ zfs snapshot mypool@backup1
确认快照已创建:
$ zfs list -t snapshot mypool NAME USED AVAIL REFER MOUNTPOINT mypool@backup1 0B - 165M -
现在已经存在一个快照,可使用 zfs send 创建代表快照内容的流。将此流存储为文件,或在另一个池上接收
必须将流重定向到文件或管道,否则会出现如下错误:
$ zfs send mypool@backup1 Error: Stream can not be written to a terminal. You must redirect standard output.
创建备份流
要备份数据集,可在使用 zfs send 时将流重定向到已挂载备份池上的文件:
$ zfs send mypool@backup1 > /backup/backup1
请确保池有足够的可用空间容纳所发送快照的大小 即快照中包含的数据总量,而非与上一个快照之间的差异容量
查看发送后两个池的空间占用变化:
$ zpool list mypool backup NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT backup 960M 161M 799M - - 8% 16% 1.00x ONLINE - mypool 960M 166M 794M - - 8% 17% 1.00x ONLINE -
zfs send 已将快照 backup1 中的所有数据传输到存储池 backup
要自动创建并发送这些快照,可配置 cron 任务
接收备份流
与作为归档文件存储不同,ZFS 可将数据接收为活动的文件系统,从而直接访问备份数据
要访问这些流中的实际数据,可使用 zfs receive 将流还原为文件和目录。
以下示例将 zfs send 与 zfs receive 结合,通过管道将数据从池 mypool 复制到另一个池 backup
先为 mypool 创建快照:
$ zfs snapshot mypool@replica1
传输完成后,可直接在接收池上使用数据 注意:只能将数据集复制到空数据集
执行全量发送与接收:
$ zfs send -v mypool@replica1 | zfs receive backup/mypool full send of mypool@replica1 estimated size is 166M total estimated size is 166M TIME SENT SNAPSHOT mypool@replica1
- 验证:
查看接收后两个池的空间占用:
$ zpool list mypool backup NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT backup 960M 331M 629M - - 12% 34% 1.00x ONLINE - mypool 960M 166M 794M - - 8% 17% 1.00x ONLINE -
查看备份目录结构:
$ ls -al /backup/ total 169330 drwxr-xr-x 3 root wheel 4 Apr 16 11:56 . drwxr-xr-x 22 root wheel 24 Apr 16 11:53 .. -rw-r--r-- 1 root wheel 173869216 Apr 16 11:54 backup1 drwxr-xr-x 2 root wheel 3 Apr 16 11:53 mypool
验证备份文件与源文件一致:
$ ls -al /backup/mypool/bsdbook.pdf -rw-r--r-- 1 root wheel 173330339 Apr 16 11:54 /backup/mypool/bsdbook.pdf
增量备份
zfs send 还可以确定两个快照之间的差异,仅发送二者之间的增量。这既节省了磁盘空间,又缩短了传输时间
将若干 非重要 文件复制到 mypool 池中便于测试:
$ cp /home/ykla/bsdbook.epub /mypool
创建第二个快照:
$ zfs snapshot mypool@replica2
查看快照的 REFER 值变化:
$ zfs list -t snapshot mypool NAME USED AVAIL REFER MOUNTPOINT mypool@backup1 0B - 165M - mypool@replica1 0B - 165M - mypool@replica2 0B - 356M -
查看增量复制前两个池的容量:
$ zpool list backup mypool NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT backup 960M 331M 629M - - 12% 34% 1.00x ONLINE - mypool 960M 356M 604M - - 12% 37% 1.00x ONLINE -
以上创建了第二个快照 replica2。此快照包含自上一个快照 replica1 以来文件系统发生的所有更改
使用 zfs send -i 并指定这两个快照对,会生成仅包含已更改数据的增量复制流。若初始快照已存在于接收端,此操作便成功
$ zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive backup/mypool send from mypool@replica1 to mypool@replica2 estimated size is 191M total estimated size is 191M TIME SENT SNAPSHOT mypool@replica2
增量发送后,查看两个池的空间占用, 备份池仅增加了差异数据量:
$ zpool list backup mypool NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT backup 960M 522M 438M - - 13% 54% 1.00x ONLINE - mypool 960M 356M 604M - - 12% 37% 1.00x ONLINE -
列出两个池及其数据集,查看空间分配:
$ zfs list -r backup mypool NAME USED AVAIL REFER MOUNTPOINT backup 522M 310M 165M /backup backup/mypool 356M 310M 356M /backup/mypool mypool 356M 476M 356M /mypool
查看备份池中的快照列表:
$ zfs list -t snapshot backup mypool NAME USED AVAIL REFER MOUNTPOINT mypool@backup1 0B - 165M - mypool@replica1 0B - 165M - mypool@replica2 0B - 356M -
增量流仅复制了已更改的数据,而非整个 replica1 仅发送差异可大幅缩短传输时间,并避免每次都复制整个池而节省磁盘空间 在通过慢速网络或按传输字节计费的网络复制时,这一优势尤为明显
至此,新的文件系统 backup/mypool 已可用,其中包含 mypool 池的数据和文件
确认增量备份文件已存在于备份池中:
$ ls -al /backup/mypool/bsdbook.epub -rw-r--r-- 1 root wheel 199611278 Apr 16 11:57 /backup/mypool/bsdbook.epub
- 使用 -p 选项可复制数据集的属性,包括压缩设置、配额和挂载点
- 使用 -R 选项可复制数据集的所有子数据集及其属性
发送和接收过程可以自动化,以便在第二个池上定期创建备份
通过 SSH 发送加密备份
通过网络发送流数据是维持远程备份的有效手段,但存在一个缺点: 经网络链路发送的数据未加密,任何人都可拦截流数据并将其还原为原始数据,而无需发送方的授权 这在通过互联网向远程主机发送数据时不可接受
为确保数据安全传输,可使用 SSH 加密发送的数据。由于 ZFS 要求将流从标准输出重定向,因此可通过 SSH 轻松管道传输
- 为确保文件系统的内容在传输过程中及在远程系统上均保持加密,可考虑使用 PEFS
首先更改一些配置并采取安全预防措施。下文描述了执行 zfs send 操作所需的步骤:
配置更改如下:
使用 ZFS 授权系统来允许每个系统上的非 root 用户执行相应的发送和接收操作。在发送系统上:
$ zfs allow -u 用户名 send,snapshot mypool
在发送和接收主机之间使用 SSH 密钥实现无密码 SSH 访问 ZFS 需要 root 用户的权限来发送和接收流。这要求以 root 身份登录到接收系统 出于安全原因,默认情况下禁止 root 登录
要挂载池,非特权用户必须拥有该目录,而且常规用户需要有挂载文件系统的权限
$ sysctl vfs.usermount=1 vfs.usermount: 0 -> 1 $ echo vfs.usermount=1 >> /etc/sysctl.conf $ zfs create recvpool/backup $ zfs allow -u 用户名 create,mount,receive recvpool/backup $ chown 用户名 /recvpool/backup
现在,非特权用户也可以接收和挂载数据集,并将 home 数据集复制到远程系统 建议使用 IP 地址或完全限定域名。接收方将数据写入 recvpool 池上的 backup 数据集
在 mypool 池上为文件系统数据集 home 创建递归快照 monday
$ zfs snapshot -r mypool/home@monday
zfs send -R 会将数据集、所有子数据集、快照、克隆以及设置一并纳入流中
$ zfs send -R mypool/home@monday | ssh 用户名@backuphost zfs recv -dvu recvpool/backup
- 输出经 SSH 管道传输到远程主机 backuphost 上等待的 zfs receive 。在 zfs recv 中:
- -d 选项会用快照名称覆盖接收方池的名称
- -u 选项使文件系统在接收方不挂载
- -v 选项可显示更多传输详情,包括耗时和数据量
书签
书签是 快照 的 轻量级引用 ,记录快照创建时刻的 时间点位置
- 与快照不同,书签不持有数据块,因此创建后不占用任何额外磁盘空间,也无需像快照那样随数据变化而消耗空间
- 书签的典型用途是作为 zfs send 增量流的源,在没有完整快照的情况下实现增量复制
书签功能需要存储池启用功能标志 bookmarks 在现代版本的 ZFS 中,该标志默认启用
创建书签
使用 zfs bookmark 命令为快照 zroot/var/tmp@test 创建书签:
$ zfs bookmark zroot/var/tmp@test zroot/var/tmp#mybookmark
也可以基于已有书签再创建书签:
$ zfs bookmark zroot/var/tmp#mybookmark zroot/var/tmp#mybookmark2
与快照的 @ 分隔符不同,书签使用 # 分隔数据集名称与书签名
列出书签
书签不会在普通的 zfs list 操作中显示。要列出书签,需使用 -t bookmark 选项:
$ zfs list -t bookmark NAME USED AVAIL REFER MOUNTPOINT zroot/var/tmp#mybookmark - - 96K - zroot/var/tmp#mybookmark2 - - 96K -
由于书签不占用空间也不可挂载,USED、AVAIL、REFER 和 MOUNTPOINT 列均显示为 -
使用书签增量发送
书签的主要价值在于增量复制。如果源系统的快照已销毁,但仍需向备份系统发送增量流,书签可替代快照作为增量源:
$ zfs send -i zroot/var/tmp#mybookmark zroot/var/tmp@test | zfs receive backup/var/tmp
管理员因此可以在销毁中间快照后仍能继续增量备份链,极大节省了存储空间
销毁书签
使用 zfs destroy 命令销毁书签,与销毁快照的语法类似,但使用 # 分隔符:
$ zfs destroy zroot/var/tmp#mybookmark2
书签销毁后,若其是某个增量发送链唯一的源,则后续的增量发送将无法使用该书签作为基准
书签与回滚
尝试回滚到非最新快照时,ZFS 会检测是否存在更新的快照和书签,并提示用户使用 -r 选项强制删除:
$ zfs rollback zroot/var/tmp@test cannot rollback to 'zroot/var/tmp@test': more recent snapshots or bookmarks exist use '-r' to force deletion of the following snapshots and bookmarks: zroot/var/tmp@test2 zroot/var/tmp@diff
书签虽然不持有数据,但代表一个时间点引用,因此会阻止回滚到比其更早的快照 除非用户明确确认需要删除这些书签
数据集、用户和组配额
- 数据集配额 用于限制 特定数据集可消耗的空间
- 引用配额 与之类似,但它仅计算 数据集本身使用的空间 ,不包含快照和子数据集
- 用户配额和组配额可防止用户或组耗尽池或数据集的全部空间
数据集配额
新增用户的家目录数据集通常会自动创建为 zroot/home/用户名 ,且 mountpoint 一般是 /home/用户名 。示例假设系统中存在用户 ykla。如下:
$ zfs list -r zroot/home NAME USED AVAIL REFER MOUNTPOINT zroot/home 356M 48.7G 96K /home zroot/home/ykla 356M 48.7G 356M /home/ykla
要对 zroot/home/ykla 强制实施 1 GB 的数据集配额:
$ zfs set quota=1G zroot/home/ykla
要对 zroot/home/ykla 强制实施 2 GB 的引用配额:
$ zfs set refquota=2G zroot/home/ykla
要删除 zroot/home/ykla 的 1 GB 配额:
$ zfs set quota=none zroot/home/ykla
用户配额
一般格式为 userquota@用户=大小 数据集或池,且用户的名称可以是以下任一格式:
- POSIX 兼容的名称,如 ykla
- POSIX 数字 ID,如 789
- SID 名称,如 ykla@example.com
- SID 数字 ID,如 S-1-123-456-789
例如,要为用户 ykla 强制实施 50 GB 的用户配额:
$ zfs set userquota@ykla=50G zroot
用户配额属性不会通过 zfs get all 显示。除非授予 userquota 特权 非 root 用户无法看到其他用户的配额 具有此特权的用户可以查看并设置所有人的配额
要查看用户配额:
$ zfs get userquota@ykla zroot NAME PROPERTY VALUE SOURCE zroot userquota@ykla 50G local
要删除所有配额:
$ zfs set userquota@ykla=none zroot
确认用户配额已清除:
$ zfs get userquota@ykla zroot NAME PROPERTY VALUE SOURCE zroot userquota@ykla none local
组配额
设置组配额的一般格式为: groupquota@组=大小 数据集或池。要将组 ykla 的配额设置为 50 GB,请使用:
$ zfs set groupquota@ykla=50G zroot
与用户配额属性类似,非 root 用户可以查看其所属组的配额 具有 groupquota 特权的用户或 root 可以查看并设置所有组的配额
确认组配额已生效:
$ zfs get groupquota@ykla zroot NAME PROPERTY VALUE SOURCE zroot groupquota@ykla 50G local
要删除组 ykla 的配额,或者确保没有设置配额,请使用:
$ zfs set groupquota@ykla=none zroot
确认组配额已清除:
$ zfs get groupquota@ykla zroot NAME PROPERTY VALUE SOURCE zroot groupquota@ykla none local
查看配额使用情况
要显示每个用户在文件系统或快照中使用的空间以及所有配额,请使用 zfs userspace 有关组信息,请使用 zfs groupspace
特权用户和 root 可以列出 zroot/home/ykla 的配额,方法是:
$ zfs get quota zroot/home/ykla NAME PROPERTY VALUE SOURCE zroot/home/ykla quota none local
保留空间
保留空间 确保数据集始终拥有可用空间(保留的空间不会分配给任何其他数据集),对保障关键数据集或日志文件的空间非常实用。reservation 属性的格式为 reservation=大小 ,以下命令将为 zroot/home/ykla 设置 10 GB 的保留空间:
$ zfs set reservation=10G zroot/home/ykla
以下命令显示 zroot/home/ykla 上已有的保留空间:
$ zfs get reservation zroot/home/ykla NAME PROPERTY VALUE SOURCE zroot/home/ykla reservation 10G local
要清除所有保留空间:
$ zfs set reservation=none zroot/home/ykla
确认保留空间已清除:
# zfs get reservation zroot/home/ykla NAME PROPERTY VALUE SOURCE zroot/home/ykla reservation none local
引用预留
refreservation 属性用于设置 引用预留 ,适用同样的原则,格式为 refreservation=size 。 以下命令将为 zroot/home/ykla 设置 1 GB 的引用预留:
$ zfs set refreservation=1G zroot/home/ykla
以下命令显示 zroot/home/ykla 上已有的引用预留:
$ zfs get refreservation zroot/home/ykla NAME PROPERTY VALUE SOURCE zroot/home/ykla refreservation 1G local
要清除所有引用预留:
$ zfs set refreservation=none zroot/home/ykla
确认引用预留已清除:
$ zfs get refreservation zroot/home/ykla NAME PROPERTY VALUE SOURCE zroot/home/ykla refreservation none local
压缩
ZFS 的数据压缩在文件系统层面实现,对上层应用透明 启用后通常既能减少磁盘占用,又能提升读写吞吐量
管理员可以通过数据集属性查看压缩效果
$ zfs get used,compressratio,compression,logicalused zroot NAME PROPERTY VALUE SOURCE zroot used 3.94G - zroot compressratio 1.94x - zroot compression on local zroot logicalused 1.73G -
将 zroot 文件系统的数据压缩算法设置为 zstd-5 级别:
$ zfs set compression=zstd-5 zroot
压缩属性变更立即生效,无需重启系统 但该属性仅对新写入的数据生效,不会自动压缩已有的数据
再次列出各个 ZFS 文件系统的数据压缩属性及其当前设置:
$ zfs get compression NAME PROPERTY VALUE SOURCE zroot compression zstd-5 local zroot/ROOT compression zstd-5 inherited from zroot zroot/ROOT/default compression zstd-5 inherited from zroot zroot/home compression zstd-5 inherited from zroot zroot/home/ykla compression zstd-5 inherited from zroot zroot/tmp compression zstd-5 inherited from zroot zroot/usr compression zstd-5 inherited from zroot zroot/usr/ports compression zstd-5 inherited from zroot zroot/usr/src compression zstd-5 inherited from zroot zroot/var compression zstd-5 inherited from zroot zroot/var/audit compression zstd-5 inherited from zroot zroot/var/crash compression zstd-5 inherited from zroot zroot/var/log compression zstd-5 inherited from zroot zroot/var/mail compression zstd-5 inherited from zroot zroot/var/tmp compression zstd-5 inherited from zroot
查看各个 ZFS 文件系统的实际数据压缩比:
$ zfs get compressratio NAME PROPERTY VALUE SOURCE zroot compressratio 2.70x - zroot/ROOT compressratio 2.68x - zroot/ROOT/default compressratio 2.68x - zroot/home compressratio 1.00x - zroot/home/ykla compressratio 1.01x - zroot/tmp compressratio 1.00x - zroot/usr compressratio 2.73x - zroot/usr/ports compressratio 1.00x - zroot/usr/src compressratio 2.73x - zroot/var compressratio 1.47x - zroot/var/audit compressratio 1.00x - zroot/var/crash compressratio 1.01x - zroot/var/log compressratio 2.90x - zroot/var/mail compressratio 1.00x - zroot/var/tmp compressratio 1.00x -
compressratio 表示已压缩数据与未压缩数据的比值。例如 2.70x 表示数据压缩到原始大小的约 37%
ZFS 提供了不同的压缩算法,各有优劣。不同压缩算法在压缩比、压缩速度与解压速度方面表现各异,需根据工作负载特征选择。
LZ4:需要池启用 lz4_compress 功能标志(GUID:org.illumos:lz4_compress),启用后即为当前默认压缩算法
LZ4 处理可压缩数据比 LZJB 快约 50%,处理不可压缩数据快三倍以上,解压速度也比 LZJB 快约 80% 在现代 CPU 上,LZ4 单核压缩速度通常超过 500 MB/s,解压速度超过 1.5 GB/s
LZJB:由 ZFS 创始人之一 Jeff Bonwick 设计,在未启用 LZ4 功能标志的旧池上是默认压缩算法
LZJB 压缩效果良好且 CPU 开销低于 GZIP
ZSTD:一种高性能压缩算法(GUID:org.freebsd:zstd_compress),兼具高压缩比与高速度
相比 GZIP 在更高速度下提供略好的压缩比,相比 LZ4 提供更好的压缩比而速度仅略慢
可通过 zstd-N (N=1~19)指定 压缩级别
zstd 等同于 zstd-3
- 可通过 zstd-fast-N 指定快速模式,其中 N 为 1–10, 20, 30, …, 100, 500, 1000 中的整数,映射为负 zstd 级别
级别越低压缩越快,1000 提供最快压缩和最低压缩比
zstd-fast 等同于 zstd-fast-1
- GZIP:流行压缩算法,主要优势在于可配置压缩级别
设置 compression 属性时,管理员可从 gzip-1(最快)到 gzip-9(最佳压缩比)之间选择,以在 CPU 时间与磁盘空间之间取得平衡
gzip 等同于 gzip-6(这也是 gzip(1) 的默认级别)
- ZLE:零长度编码,仅压缩连续零块,适用于包含大量零块的数据集
压缩与用户配额结合时可能产生意外的副作用。用户配额限制的是压缩后用户实际消耗的空间
若一个用户的配额为 10 GB,写入了 10 GB 可压缩数据,则他仍能存储更多数据 如果他后续更新某个文件(例如数据库),使用更多或更少可压缩数据时,其可用空间量将发生变化 这可能造成一种奇怪的情况:用户并未增加实际数据量(logicalused 属性),但因压缩率变化而触及配额上限
压缩与备份的交互也可能出现类似的意外效果。配额通常用于限制数据存储以确保预留足够的备份空间
由于配额不考虑压缩因素,ZFS 可能写入比未压缩备份更多的数据
去重
启用去重可节省存储空间,但需大量内存支撑去重表
在启用前应先评估工作负载是否真正需要去重 压缩在不增加额外开销的情况下即可提供大部分空间节省
并非所有数据都适合去重。如果池中的数据没有冗余,去重未必能带来节省。ZFS 可通过模拟去重来预估潜在的空间节省(必须先导出池):
$ zdb -S mypool Simulated DDT histogram: bucket allocated referenced ______ ______________________________ ______________________________ refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE ------ ------ ----- ----- ----- ------ ----- ----- ----- 1 285 35.5M 35.0M 35.0M 285 35.5M 35.0M 35.0M 2 278 34.8M 34.3M 34.3M 834 104M 103M 103M Total 563 70.3M 69.3M 69.3M 1.09K 140M 138M 138M dedup = 1.99, compress = 1.01, copies = 1.00, dedup * compress / copies = 2.02
zdb -S 完成分析后,会显示启用去重可能带来的空间节省比例
本例中 dedup 值 2.02 是一个较高的值,节省主要来自压缩 若在此池上启用去重,不仅不会节省任何空间,启用去重所需的内存开销也得不偿失
系统管理员可使用以下公式来规划存储分配,判断工作负载的重复块数量是否值得投入相应内存:
- 如果 dedup 值接近 2,则推荐去重
- 如果 dedup 接近 1,则不推荐去重
如果数据具有良好的可压缩性,空间节省可能非常可观。
由于压缩也能带来显著的性能提升,最佳实践是先启用压缩 仅在去重能提供明显节省且拥有足够内存支撑 DDT 的情况下才启用去重
要启用去重,请在目标池上设置 dedup 属性:
$ zfs set dedup=on zroot
去重仅影响写入池的新增数据,仅启用此选项不会对已写入的数据生效
新启用去重的池将显示如下:
$ zpool list zroot NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT zroot 51.5G 1.21G 50.3G - - 0% 2% 1.00x ONLINE -
DEDUP 列显示池的实际去重率。1.00x 表示数据尚未去重
下面的例子将随机生成块文件,然后将之分别复制到两个不同文件名观察去重效果:
$ dd if=/dev/urandom of=/zroot/testfile bs=1M count=100 $ cp /zroot/testfile /zroot/testfile.copy1 $ cp /zroot/testfile /zroot/testfile.copy2
要观察冗余数据的去重效果,可使用:
$ zpool list zroot NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT zroot 51.5G 1.31G 50.2G - - 0% 2% 2.99x ONLINE -
DEDUP 列显示 3.00x 的去重系数,检测并去重数据副本仅使用了三分之一的空间 空间节省潜力巨大,但需要足够的内存来跟踪去重块
还可以使用 zdb 命令:
$ zdb -DD zroot DDT-sha256: version=1 [FDT]; flags=0x03 [FLAT LOG]; rootobj=154 DDT-sha256-zap-unique: dspace=221184; mspace=163840; entries=643 DDT-log-sha256-0: flags=0x01 [FLUSHING]; obj=155; len=393216; txg=593; entries=802 DDT-log-sha256-1: flags=0x00; obj=156; len=0; txg=601; entries=0 DDT histogram (aggregated over all DDTs): bucket allocated referenced ______ ______________________________ ______________________________ refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE ------ ------ ----- ----- ----- ------ ----- ----- ----- 1 4 8.50K 8.50K 16K 4 8.50K 8.50K 16K 2 800 100M 100M 100M 2.34K 300M 300M 300M Total 804 100M 100M 100M 2.35K 300M 300M 300M dedup = 3.00, compress = 1.00, copies = 1.00, dedup * compress / copies = 3.00
| Next:委托管理 | Previous: 存储池管理 | Home: ZFS文件系统 |