最近需要在Linux下对大批量文件进行冷备份,同时希望尽量避免磁盘扇区损坏造成的影响。对于这个场景,在Windows下,具有恢复记录功能WinRAR的是一个合适的方案。虽然WinRAR有Linux的版本,但是WinRAR是非开源且收费的软件。在Linux平台下,zstd支持多线程,可以实现高速压缩和解压缩,使用par2可以创建恢复记录文件,可以基本实现WinRAR同样的功能。

在Debian/Ubuntu中,使用apt可以直接安装zstd和par2:

sudo apt-get install zstd par2

使用tar时,安装了zstd后,可以将压缩选项默认使用的gzip更换为zstd。一个将test目录打包为test.tar.zst的例子如下:

tar --zstd -cvf test.tar.zst test/

借助管道,直接使用zstd命令,也可以启用多线程压缩(如以下例子为使用4个线程):

tar -cf - test/ | zstd -vT4 >test.tar.zst

par2命令集成了创建(create)、校验(verify)和修复(repair)的功能。一个创建容量为3%恢复记录文件的例子如下:

par2 create -r3 -n1 test.tar.zst.par2 test.tar.zst

其中,-r参数为恢复记录的比例。该百分比越高,恢复记录文件的体积越大,可以应对的损坏区块比例也越大。-n1设置只创建1个冗余记录文件。如果不添加该参数,会拆分建立不同比例的恢复记录文件。

par2的其他用法可以通过par2 -h查看。

par2可以在一个恢复记录文件中对应多个原始文件,只需要在结尾增加相应原始文件的文件名即可。按照实际情况,可以不进行打包或压缩(如类似视频文件的难以进一步压缩的大文件),直接对原始文件建立恢复记录文件。

测试

为了验证zstd和par2可以应对少量硬盘扇区损坏的情况,一个测试如下。该测试首先创建2个test文件,之后将其打包,然后创建恢复文件。之后将打包的zstd文件的一个扇区置0,进而测试par2的恢复功能。
首先创建测试文件:

mkdir test
dd if=/dev/urandom of=test/test.bin.1 bs=4k count=16k
dd if=/dev/urandom of=test/test.bin.2 bs=4k count=16k
md5sum test/test.bin.* | tee test/md5sum
# 两个随机文件的校验值如下:
# e9908eb2d202e34fc46628a0cc8eb7fd  test/test.bin.1
# f41cfe8765bad77b4846452743212d8e  test/test.bin.2

打包:

tar --zstd -cvf test.tar.zst test/

创建3%恢复记录:

par2 create -r3 -n1 test.tar.zst.par2 test.tar.zst

此时,par2的输出如下:

Block size: 67112
Source file count: 1
Source block count: 2000
Recovery block count: 60
Recovery file count: 1

Opening: test.tar.zst
Computing Reed Solomon matrix.
Constructing: done.
Wrote 4026720 bytes to disk
Writing recovery packets
Writing verification packets
Done

当前路径下创建了包含描述信息的test.tar.zst.par2和包含实际冗余数据的test.tar.zst.vol00+60.par2两个文件。
直接对文件进行校验,命令为:

par2 verify *.par2

输出结果如下:

Loading "test.tar.zst.par2".
Loaded 4 new packets
Loading "test.tar.zst.vol00+60.par2".
Loaded 60 new packets including 60 recovery blocks
Loading "test.tar.zst.par2".
No new packets found
Loading "test.tar.zst.vol00+60.par2".
No new packets found

There are 1 recoverable files and 0 other files.
The block size used was 67112 bytes.
There are a total of 2000 data blocks.
The total size of the data files is 134223394 bytes.

Verifying source files:

Opening: "test.tar.zst"
Target: "test.tar.zst" - found.

All files are correct, repair is not required.

提示所有文件校验通过,无需修复。
对test.tar.zst的一个512B的块填充0,模拟扇区损坏的效果:

dd if=/dev/zero of=test.tar.zst bs=512 count=1 seek=12k conv=notrunc

再进行解压:

tar --zstd -xvf test.tar.zst

会提示检测到区块损坏;

test/
test/md5sum
test/test.bin.2
/*stdin*\ : Decoding error (36) : Corrupted block detected
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now

使用par2的repair命令进行修复:

par2 repair *.par2

输出信息如下,提示有1个区块损坏:

Loading "test.tar.zst.par2".
Loaded 4 new packets
Loading "test.tar.zst.vol00+60.par2".
Loaded 60 new packets including 60 recovery blocks
Loading "test.tar.zst.par2".
No new packets found
Loading "test.tar.zst.vol00+60.par2".
No new packets found

There are 1 recoverable files and 0 other files.
The block size used was 67112 bytes.
There are a total of 2000 data blocks.
The total size of the data files is 134223394 bytes.

Verifying source files:

Opening: "test.tar.zst"
Target: "test.tar.zst" - damaged. Found 1999 of 2000 data blocks.

Scanning extra files:


Repair is required.
1 file(s) exist but are damaged.
You have 1999 out of 2000 data blocks available.
You have 60 recovery blocks available.
Repair is possible.
You have an excess of 59 recovery blocks.
1 recovery blocks will be used to repair.

Computing Reed Solomon matrix.
Constructing: done.
Solving: done.

Wrote 134223394 bytes to disk

Verifying repaired files:

Opening: "test.tar.zst"
Target: "test.tar.zst" - found.

Repair complete.

最后结果为修复成功,修复之前的损坏文件会自动被备份为test.tar.zst.1。
再次进行解压,即可正常解压出原有的文件:

tar --zstd -xvf test.tar.zst
# test/
# test/md5sum
# test/test.bin.2
# test/test.bin.1

重新对test.bin.1和test.bin.2进行校验,md5sum与原md5sum校验值一致。

zstd压缩率测试

zstd支持多个压缩等级(1到19),直接使用-#将#替换为压缩等级即可,默认压缩等级为3,压缩等级越高,压缩后的数据越小,但耗费的处理器资源约高。
此处测试了不同压缩等级的情况下,文件的压缩率和压缩时间(此处记time命令的user部分,即统计用户态的CPU时间,以忽略系统IO耗费的时间),仅供参考。压缩的原始文件为服务器/usr/bin下的所有文件(即压缩对象主要是Linux的脚本文件和可执行程序,共509MB),并使用tar命令打包,处理器为Intel Xeon 6271C,测试结果如下

gzip      164M (26.117s)
zstd1     184M (2.298s)
zstd2     173M (2.803s)
zstd3     165M (4.025s)
zstd4     164M (4.788s)
zstd5     160M (7.972s)
zstd6     159M (9.279s)
zstd7     156M (12.160s)
zstd8     154M (14.599s)
zstd9     154M (15.988s)
zstd10    152M (19.021s)
zstd11    152M (22.754s)
zstd12    151M (28.193s)
zstd13    151M (42.998s)
zstd14    151M (50.883s)
zstd15    150M (1m1.696s)
zstd16    147M (1m33.183s)
zstd17    138M (1m59.670s)
zstd18    131M (2m33.387s)
zstd19    131M (3m54.914s)

可以看出,在该压缩场景下,zstd最低的压缩等级的压缩速度要远快于gzip,而压缩后的容量仅比gzip大了约10%;而默认的压缩等级可以获得和gzip近似的压缩率,而耗时只有gzip的约15%;若将压缩等级调整为11或12,压缩时间与gzip接近,但zstd可以实现更好的压缩率。
此处,测试使用的脚本如下:

#!/bin/bash
time tar -C / -cf - usr/bin | gzip -c > bin.tar.gz
for i in $(seq 1 19)
do
  echo zstd$i:
  time tar -C / -cf - usr/bin | zstd -${i}T1 > bin.tar.zstd${i}
done

标签: Linux

添加新评论