专栏首页运维之美用好了下一代文件系统 Btrfs 这些新特性,从此数据安全乐无忧!

用好了下一代文件系统 Btrfs 这些新特性,从此数据安全乐无忧!

对于大部分文件系统来说,在磁盘上创建好文件系统,然后再挂载到系统中去就完事了。但对于 Btrfs 来说,除了在格式化和挂载的时候指定不同的参数外,还支持很多其他的功能。比如:管理多块硬盘、支持 LVM 和 RAID 等,具体的可以参考它的「官方文档」或者「Linux 下常见文件系统对比」。

Btrfs 是 Oracle 07 年基于 GPL 协议开源的 Linux 文件系统,其目的是替换传统的 Ext3、Ext4 系列文件系统。Ext 系列文件系统存在着诸多问题,比如反删除能力有限等;而 Btrfs 在解决问题同时提供了更加强大的高级特性。

Btrfs 特性

Btrfs 在文件系统级别支持写时复制 (COW) 机制,并且支持快照 (增量快照)、支持对单个文件快照;同时支持单个超大文件、文件检查、内建 RAID;支持 B 树子卷 (组合多个物理卷,多卷支持) 等。具体如下:

Btrfs 核心特性:

  • 多物理卷支持:Btrfs 可有多个物理卷组成 (类似 LVM);支持 RAID 以及联机 添加、删除、修改
  • 写时复制更新机制 (COW):复制、更新、替换指针,而非传统意义上的覆盖
  • 支持数据及元数据校验码:Checksum 机制
  • 支持创建子卷:Subvolume 机制,同时可多层创建
  • 支持快照:基于 COW 实现快照,并且相对于 LVM 可以实现快照的快照 (增量快照)
  • 支持透明压缩:后台自动压缩文件(消耗一定 CPU),对前端程序透明

Btrfs 是 Linux 下大家公认的将会替代 ext4 的下一代文件系统,功能非常强大。本篇不会介绍 Btrfs 的原理,也不会介绍 Btrfs 的所有功能,只是挑了其中的 Subvolume 和 Snapshot 这两个特性来进行介绍。

本篇所有例子都在 Ubuntu-Server-X86_64 16.04 下执行通过。

准备环境

先创建一个虚拟的硬盘,然后将它格式化成 Btrfs,最后将它挂载到目录 /mnt/btrfs 。

# 为了简单起见,这里只使用一块硬盘来做测试(Btrf s可以管理多块硬盘或分区)。
# 新建一个文件,用来虚拟一块硬盘。
dev@ubuntu:~$ fallocate -l 512M /tmp/btrfs.img

# 在上面创建 Btrfs 文件系统
dev@ubuntu:~$ mkfs.btrfs /tmp/btrfs.img
btrfs-progs v4.4
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:               fd5efcd3-adc2-406b-a684-e6c87dde99a1
Node size:          16384
Sector size:        4096
Filesystem size:    512.00MiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP              40.00MiB
  System:           DUP              12.00MiB
SSD detected:       no
Incompat features:  extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1   512.00MiB  /tmp/btrfs.img

# 创建文件夹并挂载
dev@ubuntu:~$ sudo mkdir /mnt/btrfs
dev@ubuntu:~$ sudo mount /tmp/btrfs.img /mnt/btrfs

# 修改权限,这样后面的部分操作就不再需要 sudo
dev@ubuntu:~$ sudo chmod 777 /mnt/btrfs

Subvolume

可以把 Subvolume 理解为一个虚拟的设备,由 Btrfs 管理,创建好了之后就自动挂载到了 Btrfs 文件系统的一个目录上,所以我们在文件系统里面看到的 Subvolume 就是一个目录,但它是一个特殊的目录,具有挂载点的一些属性。

新创建的 Btrfs 文件系统会创建一个路径为 “/” 的默认 Subvolume,即 Root Subvolume。其 ID 为 5(别名为 0),这是一个 ID 和目录都预设好的 Subvolume。

# 这里从 mount 的参数 “subvolid=5,subvol=/” 就可以看出来默认的 Root Subvolume 的 id 为 5,路径为 “/” 。
dev@debian:/mnt/btrfs$ mount|grep btrfs
/dev/loop1 on /mnt/btrfs type btrfs (rw,relatime,space_cache,subvolid=5,subvol=/)

创建 Subvolume

这里我们将会利用 Btrfs 提供的工具创建两个新 Subvolume 和两个文件夹,来看看他们之间的差别

dev@ubuntu:~$ cd /mnt/btrfs
# btrfs 命令是 Btrfs 提供的应用层工具,可以用来管理 Btrfs。
# 这里依次创建两个 Subvolume,创建完成之后会自动在当前目录下生成两个目录。
dev@ubuntu:/mnt/btrfs$ btrfs subvolume create sub1
Create subvolume './sub1'
dev@ubuntu:/mnt/btrfs$ btrfs subvolume create sub2
Create subvolume './sub2'

# 创建两个文件夹
dev@ubuntu:/mnt/btrfs$ mkdir dir1 dir2

# 在sub1、sub2 和 dir1 中分别创建一个文件
dev@ubuntu:/mnt/btrfs$ touch dir1/dir1-01.txt
dev@ubuntu:/mnt/btrfs$ touch sub1/sub1-01.txt
dev@ubuntu:/mnt/btrfs$ touch sub2/sub2-01.txt

# 最后看看目录结构,是不是看起来 sub1 和 dir1 没什么区别?
dev@ubuntu:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
├── dir2
├── sub1
│   └── sub1-01.txt
└── sub2
    └── sub2-01.txt

不过由于每个 Subvolume 都是一个单独的虚拟设备,所以无法跨 Subvolume 建立硬链接。

# 虽然 sub1 和 sub2 属于相同的 Btrfs 文件系统,并且在一块物理硬盘上。但由于他们属于不同的 Subvolume,所以在它们之间建立硬链接失败。
dev@ubuntu:/mnt/btrfs$ ln ./sub1/sub1-01.txt ./sub2/
ln: failed to create hard link './sub2/sub1-01.txt' => './sub1/sub1-01.txt': Invalid cross-device link

删除 Subvolume

Subvolume 不能用 rm 命令来删除的,只能通过 btrfs 命令来删除。

# 普通的目录通过 rm 命令就可以被删除
dev@ubuntu:/mnt/btrfs$ rm -r dir2

# 通过 rm 命令删除 Subvolume 就会失败
dev@ubuntu:/mnt/btrfs$ sudo rm -r sub2
rm: cannot remove 'sub2': Operation not permitted

# 需要通过 btrfs 命令才能删除,删除 sub2 成功(就算 Subvolume 里面有文件也能被删除)
dev@ubuntu:/mnt/btrfs$ sudo btrfs subvolume del sub2
Delete subvolume (no-commit): '/mnt/btrfs/sub2'
dev@ubuntu:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
└── sub1
    └── sub1-01.txt

上面删除的时候可以看到这样的提示:Delete subvolume (no-commit),表示 Subvolume 被删除了,但没有提交。意思是在内存里面生效了,但磁盘上的内容还没删,意味着如果这个时候系统 Crash 掉,这个 Subvolume 有可能还会回来。Btrfs 这样做的好处是删除速度很快,不会影响使用,缺点是有可能在后台 Commit 的过程中系统挂掉,导致 Commit 失败。

为了确保 Subvolume 里的数据被真正的从磁盘上移除掉,可以在删除 Subvolume 的时候指定 -c 参数,这样 btrfs命令会等提交完成之后再返回。

dev@ubuntu:/mnt/btrfs$ sudo btrfs subvolume del -c sub2Delete subvolume (commit): '/mnt/btrfs/sub2'

挂载 Subvolume

Subvolume 可以直接通过 mount 命令进行挂载,和挂载其它设备没什么区别,具体的挂载参数请查看参考官方文档。

# 创建一个用于挂载点的目录dev@ubuntu:/mnt/btrfs$ sudo mkdir /mnt/sub1
# 先查看待挂载的 Subvolume 的 iddev@debian:/mnt/btrfs$ sudo btrfs subvolume list /mnt/btrfs/ID 256 gen 9 top level 5 path sub1
# 通过 -o 参数来指定要挂载的 Subvolume 的 ID# 通过路径来挂载也是一样的效果:sudo mount -o subvol=/sub1 /tmp/btrfs.img /mnt/sub1/dev@debian:/mnt/btrfs$ sudo mount -o subvolid=256 /tmp/btrfs.img /mnt/sub1/dev@debian:/mnt/btrfs$ tree /mnt/sub1//mnt/sub1/└── sub1-01.txt

设置 Subvolume 只读

Subvolume 可以被设置成只读状态。

# 通过 btrfs property 可以查看和修改 Subvolume 的只读状态# 默认情况下,Subvolume 的只读属性为 false,即允许写dev@ubuntu:/mnt/btrfs$ btrfs property get -ts ./sub1/ro=false
# 将 sub1 的只读属性设置成 truedev@ubuntu:/mnt/btrfs$ btrfs property set -ts ./sub1/ ro truedev@ubuntu:/mnt/btrfs$ btrfs property get -ts ./sub1ro=true
# 写文件失败,提示文件系统只读dev@ubuntu:/mnt/btrfs$ touch ./sub1/sub1-02.txttouch: cannot touch './sub1/sub1-02.txt': Read-only file system
# 将sub1的状态改回去,以免影响后续测试dev@ubuntu:/mnt/btrfs$ btrfs property set -ts ./sub1/ ro false

Snapshot

可以在 Subvolume 的基础上制作快照,几点需要注意:

  • 默认情况下 Subvolume 的快照是可写的
  • 快照是特殊的 Subvolume,具有 Subvolume 的属性。所以快照也可以通过 mount 挂载,也可以通过 btrfs property 命令设置只读属性
  • 由于快照的本质就是一个 Subvolume ,所以可以在快照上面再做快照

在 Subvolume 上做了快照后,Subvolume 和快照就会共享所有的文件。只有当文件更新的时候,才会触发 COW(copy on write),所以创建快照很快,基本不花时间。并且 Btrfs 的 COW 机制很高效,就算多个快照共享一个文件,更新这个文件也和更新一个普通文件差不多的速度。

如果用过 Git 的话,就能很容易理解 Btrfs 里的快照,可以把 Subvolume 理解为 Git 里面的 master 分支,而快照就是从 master checkout 出来的新分支,于是快照跟 Git 里的分支有类似的特点:

  • 创建快照几乎没有开销
  • 可以在快照的基础上再创建快照
  • 当前快照里面的修改不会影响其它快照
  • 快照可以被删除

当然 Subvolume 也可以像 Git 里的 master 一样被删除。

创建快照

# 在 Root Subvolume 的基础上创建一个快照# 默认情况下快照是可写的,如果要创建只读快照,需要加上 -r 参数dev@debian:/mnt/btrfs$ sudo btrfs subvolume snapshot ./ ./snap-rootCreate a snapshot of './' in './snap-root'
# 创建完成后,可以看到我们已经有了两个 Subvolumedev@debian:/mnt/btrfs$ sudo btrfs subvolume list ./ID 256 gen 11 top level 5 path sub1ID 257 gen 13 top level 5 path snap-root
# 我们可以通过指定 -s 参数来只列出快照dev@debian:/mnt/btrfs$ sudo btrfs subvolume list -s ./ID 257 gen 10 cgen 10 top level 5 otime 2017-03-05 21:46:03 path snap-root
# 再来看看快照 snap-root 中的文件,可以看到有 dir1 及下面的文件,但看不到 sub1 下的文件,那是因为 sub1 是一个subvolume。在做一个 Subvolume 的快照的时候,不会将它里面的 Subvolume 也做快照dev@debian:/mnt/btrfs$ tree ./snap-root./snap-root├── dir1│   └── dir1-01.txt└── sub1
# 创建 sub1 的一个快照,可以看到 sub1 里面的文件出现在了快照里面dev@debian:/mnt/btrfs$ sudo btrfs subvolume snapshot ./sub1/ ./snap-sub1Create a snapshot of './sub1/' in './snap-sub1'
# 然后在 sub1 和它的快照 snap-sub1 下面各自创建一个文件,会发现它们之间不受影响dev@debian:/mnt/btrfs$ touch snap-sub1/snap-sub1-01.txtdev@debian:/mnt/btrfs$ touch sub1/sub1-02.txtdev@debian:/mnt/btrfs$ tree.├── dir1│   └── dir1-01.txt├── snap-root│   ├── dir1│   │   └── dir1-01.txt│   └── sub1├── snap-sub1│   ├── snap-sub1-01.txt│   └── sub1-01.txt└── sub1    ├── sub1-01.txt    └── sub1-02.txt

删除快照

删除快照和删除 Subvolume 是一样的,没有区别。

dev@debian:/mnt/btrfs$ sudo btrfs subvolume del snap-rootDelete subvolume (no-commit): '/mnt/btrfs/snap-root'dev@debian:/mnt/btrfs$ sudo btrfs subvolume del snap-sub1Delete subvolume (no-commit): '/mnt/btrfs/snap-sub1'dev@debian:/mnt/btrfs$ tree.├── dir1│   └── dir1-01.txt└── sub1    ├── sub1-01.txt    └── sub1-02.txt

Default Subvolume

可以设置 Btrfs 分区的默认 Subvolume,即在挂载磁盘的时候,可以只让分区中的指定 Subvolume 对用户可见。看下面的例子:

# 查看 sub1 的IDdev@debian:/mnt/btrfs$ sudo btrfs subvolume list ./ID 256 gen 14 top level 5 path sub1
# 将 sub1 设置为当前 Btrfs 文件系统的默认 Subvolumedev@debian:/mnt/btrfs$ sudo btrfs subvolume set-default 256 /mnt/btrfs/
# 重新将虚拟硬盘挂载到一个新目录dev@debian:/mnt/btrfs$ sudo mkdir /mnt/btrfs1dev@debian:/mnt/btrfs$ sudo mount /tmp/btrfs.img /mnt/btrfs1/
# 这里将只能看到 sub1 下的文件dev@debian:/mnt/btrfs$ tree /mnt/btrfs1/mnt/btrfs1├── sub1-01.txt└── sub1-02.txt
# 由于 Btrfs 原来的默认 Subvolume 是 Root Subvolume,其 ID 是5(也可以通过 0 来标识),所以我们可以通过同样的命令将默认 Subvolume 再改回去dev@debian:/mnt/btrfs$ sudo btrfs subvolume set-default 0 /mnt/btrfs/

Default Subvolume 有什么用呢?

利用 Snapshot 和 Default Subvolume,可以很方便的实现不同系统版本的切换。比如将系统安装在一个 Subvolume 下面,当要做什么危险操作的时候,先在 Subvolume 的基础上做一个快照 A。如果操作成功,那么什么都不用做(或者把 A 删掉),继续用原来的 Subvolume,A 不被删掉也没关系,多一个快照在那里也不占空间。如果操作失败,那么可以将 A 设置成 Default Subvolume,并将原来的 Subvolume 删除,这样就相当于系统回滚。

有了这样的功能后,Linux 的每次操作都能回滚,养成在修改操作前做 Snapshot 的习惯,就再也不用担心 rm 误删文件了。

现在有些发行版已经有了类似的功能,如 Ubuntu,将安装工具 Apt 和 Btrfs 结合,自动的在安装软件之前打一个 Snapshot。然后安装软件,如果成功,删除新的 Snapshot,如果失败,修改 Default Subvolume 为新的 Snapshot,删除掉原来的 Snapshot,这样对系统没有任何影响,并且所有操作对用户是透明的。

随着 Btrfs 的成熟和普及,相信会改变一些我们使用 Linux 的习惯。

延伸阅读

btrfs 相关命令

管理 btrfs 使用 btrfs 命令,该命令包含诸多子命令已完成不同的功能管理,常用命令如下:

  • btrfs 文件系统属性查看:btrfs filesystem show
  • 调整文件系统大小:btrfs filesystem resize +10g MOUNT_POINT
  • 添加硬件设备:btrfs filesystem add DEVICE MOUNT_POINT
  • 均衡文件负载:btrfs blance status|start|pause|resume|cancel MOUNT_POINT
  • 移除物理卷(联机、自动移动):btrfs device delete DEVICE MOUNT_POINT
  • 动态调整数据存放机制:btrfs balance start -dconvert=RAID MOUNT_POINT
  • 动态调整元数据存放机制:btrfs balance start -mconvert=RAID MOUNT_POINT
  • 动态调整文件系统数据数据存放机制:btrfs balance start -sconvert=RAID MOUNT_POINT
  • 创建子卷:btrfs subvolume create MOUNT_POINT/DIR
  • 列出所有子卷:btrfs subvolume list MOUNT_POINT
  • 显示子卷详细信息:btrfs subvolume show MOUNT_POINT
  • 删除子卷:btrfs subvolume delete MOUNT_POIN/DIR
  • 创建子卷快照(子卷快照必须存放与当前子卷的同一父卷中):btrfs subvolume snapshot SUBVOL PARVOL
  • 删除快照同删除子卷一样:btrfs subvolume delete MOUNT_POIN/DIR

相关阅读链接

  1. Btrfs 官方文档:https://btrfs.wiki.kernel.org/index.php/Main_Page
  2. Linux 下常见文件系统对比 :https://segmentfault.com/a/1190000008481493
  3. Btrfs 官方挂载硬盘文档:https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#MOUNT_OPTIONS

参考文档

  1. https://segmentfault.com/a/1190000008605135
  2. https://mritd.me/2017/03/20/btrfs-note/

本文分享自微信公众号 - 运维之美(Hi-Linux),作者:wuyangchun

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 我建立了一个「玩转 Linux」的圈子,这里有好玩、好用、好看的各种新奇内容哟!

    微信搜索时代到来了,微信搜一搜功能再一次进行了升级。其中增加了「微信圈子」功能,大家都可以根据自己的兴趣爱好在里面创造自己的社交圈,让志同道合的小伙伴们可以一起...

    iMike
  • 谈谈异地多活架构

    无论是高可用计算架构,还是高可用存储架构,其本质的设计目的都是为了解决部分服务器故障的场景下,如何保证系统能够继续提供服务。但在一些极端场景下,有可能所有服务...

    iMike
  • 可能是目前全网最好的全平台去广告指南,让你从此告别广告的烦恼!( 强烈建议收藏 )

    广告营收占据了 Google 利润的大部分,然而 Google 却在自家的浏览器 Chrome 中加入了去广告功能并默认开启。

    iMike
  • Android7.0 分屏下 Activity 与 Fragment 生命周期(一)

    和尚前段时间整理了一篇关于我们真的了解 Activity 与 Fragment 的生命周期吗?的小博文,整理了基础版的关于 Activity 与 F...

    阿策
  • R语言18讲(三)

    ? 我们在做数据分析工作的前提,当然是得有数据,巧妇难为无米之炊,所以数据的获取和产生是非常重要和基础的,然而,在当前互联网时代,信息非常的膨胀,我们获取...

    小莹莹
  • EXP/IMP迁移案例,IMP遭遇导入表的表空间归属问题

    1.确认迁移需求:源数据库cssf 用户所有表和数据迁移到目标数据库新建用户cssf_gt下,表空间为dbs_cssf_gt。

    Alfred Zhao
  • 对于数据库--还有同学反映太抽象

    那你把数据库想象为一个教学楼(数据库); 教学楼的每一间教室想象为一个数据表(表); 教室里面的学生想象为数据(具体数据); 我们对数据库中数据的---增删改查...

    前朝楚水
  • Golang语言社区--【数据库知识】从关系型数据库到非关系型数据库

    1. 关系型数据库 关系型数据库,是指采用了关系模型来组织数据的数据库。 关系模型是在1970年由IBM的研究员E.F.Codd博士首先提出的,在之后的几十年中...

    李海彬
  • 初识数据库

    一、数据库概念 数据库(Database,简称DB)   数据库技术是计算机应用领域中非常重要的技术,它产生于20世纪60年代末,是数据管理的最新技术,也是软件...

    新人小试
  • 如何不宕机实现数据库迁移

    由于业务的扩展或者其他原因,常常会有迁移系统数据库的场景,对于有大量用户7*24小时不间断使用的系统,如何不宕机实现数据库迁移,这是个很有挑战的话题。

    Bruce Li

扫码关注云+社区

领取腾讯云代金券