备份 Ext4 分区的正确姿势

2022-07-27 15:13:01 +08:00
 monetto

如题,有一个 Ubuntu 系统的 U 盘,想要做全盘备份,但是看了下 Diskgenius 全盘备份功能必须 付费版才能使用。

由于 U 盘比较大( 256G ),无法使用 dd 命令进行备份(也需要 256G 空间以供备份)

求大佬,大容量 Ext4 分区备份的正确姿势是什么

6016 次点击
所在节点    Linux
72 条回复
yjd
2022-07-28 00:51:09 +08:00
专业软件 UFS Explorer Professional 备份成 VHD 是免费的。
codehz
2022-07-28 01:58:27 +08:00
@pagxir 我建议直接看源码
https://github.com/torvalds/linux/blob/master/block/fops.c#L410-L422 给出了块设备的 readahead 的实现
https://github.com/torvalds/linux/blob/master/mm/readahead.c#L157-L176 这个位置下断点,然后开 gdb 调试引导,执行命令 cat /dev/sda > /dev/null ,就可以发现确实用到了这里的 readahead
调用栈是 blkdev_readahead | read_pages | page_cache_ra_unbounded | do_page_cache_ra | page_cache_async_ra | filemap_readahead | filemap_get_pages | filemap_read | blkdev_read_iter | call_read_iter | new_sync_read | vfs_read | ksys_read | do_syscall_x64 | do_syscall_64 | entry_SYSCALL_64
每个函数的含义都可以自己查看,linux 没有魔法,不可能说源码里写了 page cache 还能不用的
tril
2022-07-28 04:52:46 +08:00
@codehz 因为搜到的方法都是 dd+gzip ,说明用的人不少。没见过谁指出这方法有什么缺陷,说明它确实管用。既然能用而且又没问题,那就这样啦 XD
mingl0280
2022-07-28 05:33:02 +08:00
clonezilla 啊,clonezilla
pagxir
2022-07-28 07:50:00 +08:00
@codehz 大概率你的盘已经挂了文件系统。不可能有 readahead 的。你可以试试看,第二次对 sda 读会变成对内存读了。
bthulu
2022-07-28 08:16:53 +08:00
装一个百度云盘, 开会员, 打开自动备份文件夹就行了
pagxir
2022-07-28 08:31:50 +08:00
$ time dd if=/dev/sdb bs=80M status=progress|dd ibs=8M obs=80M|lz4 -c | dd of=/dev/null bs=2M
31611420672 字节 (32 GB, 29 GiB) 已复制,391 s ,80.9 MB/s
输入了 376+1 块记录
输出了 376+1 块记录
31611420672 字节 (32 GB, 29 GiB) 已复制,391.608 s ,80.7 MB/s
输入了 0+482352 块记录
输出了 376+1 块记录
31611420672 字节 (32 GB, 29 GiB) 已复制,391.695 s ,80.7 MB/s
输入了 0+424343 块记录
输出了 0+424343 块记录
27642280069 字节 (28 GB, 26 GiB) 已复制,391.697 s ,70.6 MB/s

real 6m31.704s
user 0m27.970s
sys 1m28.536s
$ lz4 -c < /dev/sdb | dd of=/dev/null bs=2M status=progress
27642280069 字节 (28 GB, 26 GiB) 已复制,465 s ,59.4 MB/s
输入了 0+424180 块记录
输出了 0+424180 块记录
27642280069 字节 (28 GB, 26 GiB) 已复制,465.335 s ,59.4 MB/s

没挂文件系统下的二次读:

$ dd if=/dev/sdb of=/dev/null bs=2M count=1024
输入了 1024+0 块记录
输出了 1024+0 块记录
2147483648 字节 (2.1 GB, 2.0 GiB) 已复制,27.1303 s ,79.2 MB/s
$ dd if=/dev/sdb of=/dev/null bs=2M count=1024
输入了 1024+0 块记录
输出了 1024+0 块记录
2147483648 字节 (2.1 GB, 2.0 GiB) 已复制,26.6422 s ,80.6 MB/s
$ dd if=/dev/sdb of=/dev/null bs=2M count=100
输入了 100+0 块记录
输出了 100+0 块记录
209715200 字节 (210 MB, 200 MiB) 已复制,2.85232 s ,73.5 MB/s

挂文件系统下的二次读:
$ dd if=/dev/sdb of=/dev/null bs=2M count=100
输入了 100+0 块记录
输出了 100+0 块记录
209715200 字节 (210 MB, 200 MiB) 已复制,2.77269 s ,75.6 MB/s
$ dd if=/dev/sdb of=/dev/null bs=2M count=100
输入了 100+0 块记录
输出了 100+0 块记录
209715200 字节 (210 MB, 200 MiB) 已复制,0.0475682 s ,4.4 GB/s

之前一直想搞某个功能,结果就是在这个地方搞不下去了。从这个数据结果看,我不认为 readahead 跟 page cache 生效了。至于你说的代码,我没看过。
codehz
2022-07-28 09:23:10 +08:00
@pagxir 完全没有文件系统,是临时创建全 0 的磁盘镜像,然后 shell 是从 initrd 里来的,也就是从头到尾就没有磁盘被挂载
damenlysu
2022-07-28 09:29:57 +08:00
read without dirctIO flag 没挂文件系统的 block device 也是会经过 page cahe 的,block 层那边提交 bio 的时候会过 page cahe ,所以应该也有 readahead 。
godev2021
2022-07-28 09:38:19 +08:00
www.fsarchiver.org 可以在备份的时候压缩
junyee
2022-07-28 09:53:50 +08:00
看大佬们互怼真能学到很多知识,
请继续!
raptor
2022-07-28 10:04:51 +08:00
dd 加 gzip 不是常规操作么?
PTLin
2022-07-28 10:51:12 +08:00
partclone
codehz
2022-07-28 10:57:08 +08:00
@pagxir 有一个非常简单的方法能测试 readahead 有没有生效(
就是把它关闭了
echo 0 > /sys/block/sda/queue/read_ahead_kb
然后再运行 dd if=/dev/sda of=/dev/null bs=1M
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 1.039096 seconds, 15.4MB/s
而设置成默认 128 是这个效果
/ # echo 128 > /sys/block/sda/queue/read_ahead_kb
/ # dd if=/dev/sda of=/dev/null bs=1M
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 0.127543 seconds, 125.4MB/s
而用极为夸张的 1048576 则是这个效果(可见不是越大越好)
/ # echo 1048576 > /sys/block/sda/queue/read_ahead_kb
/ # dd if=/dev/sda of=/dev/null bs=1M
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 0.121848 seconds, 131.3MB/s

(测试中的磁盘是 qemu 参数 -drive file=/tmp/test.img,format=raw 挂载的,qemu 本身没有启用 kvm 模式,因此性能较低但是比较稳定)

此外关于 gzip 管道的问题,我也做了一个测试,当磁盘速度不是瓶颈(且 readahead 没被禁用的情况下)
/ # time dd if=/dev/sda of=/dev/null bs=1048576
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 0.129122 seconds, 123.9MB/s
real 0m 0.14s
user 0m 0.00s
sys 0m 0.13s
直接用 gzip
/ # time sh -c 'gzip -c /dev/sda > /dev/null'
real 0m 0.87s
user 0m 0.76s
sys 0m 0.10s
加个 dd 管道
/ # time sh -c 'dd if=/dev/sda bs=1048576 | gzip > /dev/null'
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 1.008238 seconds, 15.9MB/s
real 0m 1.03s
user 0m 0.79s
sys 0m 0.23s
可见速度反而还下降了(我也测试过其他 bs 值,几乎不对结果产生影响,始终大于 1 秒,这个模拟下 1048576 已经是接近最优的值了)
有趣的是,如果这时候用 dd 的 direct 模式手动绕过缓存
/ # time sh -c 'dd if=/dev/sda bs=1048576 iflag=direct | gzip > /dev/null'
16+0 records in
16+0 records out
16777216 bytes (16.0MB) copied, 0.959065 seconds, 16.7MB/s
real 0m 0.98s
user 0m 0.78s
sys 0m 0.18s
就小于 1 秒了,可惜还是没有 gzip 直接压缩快(
yanqiyu
2022-07-28 12:35:58 +08:00
虽然大家都在讨论 ra 的问题,但是纯粹的 dd/gzip 最大的问题难道不是把没使用的 block 也给备份了?浪费时间和空间?
monetto
2022-07-28 12:50:28 +08:00
@yanqiyu 是的,所以最后用了 DiskGenius 单分区备份... ORZ
YSMAN
2022-07-28 14:11:10 +08:00
@junyee 同意
pagxir
2022-07-28 15:36:26 +08:00
@codehz 如果不加 dd 就直接 gzip 更快,那是因为瓶颈在 CPU ,没有 dd 意味更少的进程调度开销自然更快。并且加 dd 一般是要加两个 dd 才会起到缓冲预读的效果。
pagxir
2022-07-28 15:40:01 +08:00
@codehz 你在 qemu 中还不开 kvm ,数据基本上没啥意义,因为瓶颈在 CPU ,连 IO 速度也被 CPU 给限制了
codehz
2022-07-28 16:11:30 +08:00
@pagxir 加两个不是更慢???
dd 也没开啥边读边写的操作啊,从上面 strace 的结果也可以看到,只是单纯的 read 固定长度然后 write ,这些全都是阻塞操作,没有使用 aio/io_uring ,多线程读写等的操作(源代码里也没有引用线程有关的东西),而 linux pipe 的默认缓冲区只有 64kb ,也就是读取端没有读完的话,写入端写入最多 64k 就会阻塞,即便你把缓冲区选择较小的范围,那也只是凭空增加系统调用数量,因为 gzip 仍然是一次性请求至少 64k (来自源代码,或者你也可以自己 strace 查看),因此 dd 开的越多只会越慢,不知道你怎么弄出“缓冲预读”的功能的
开 kvm 会让数据波动范围变大,除此之外不能改变结论,我特意选择小文件以缩小差距

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/869026

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX