我刚刚做了一个小实验,我创建了一个包含重复文件的 tar 存档,看看它是否会被压缩,令我惊讶的是,它并没有被压缩!详情如下(结果缩进以方便阅读):
$ dd if=/dev/urandom bs=1M count=1 of=a
1+0 records in
1+0 records out
1048576 bytes (1.0 MB) copied, 0.114354 s, 9.2 MB/s
$ cp a b
$ ln a c
$ ll
total 3072
-rw-r--r-- 2 guido guido 1048576 Sep 24 15:51 a
-rw-r--r-- 1 guido guido 1048576 Sep 24 15:51 b
-rw-r--r-- 2 guido guido 1048576 Sep 24 15:51 c
$ tar -c * -f test.tar
$ ls -l test.tar
-rw-r--r-- 1 guido guido 2109440 Sep 24 15:51 test.tar
$ gzip test.tar
$ ls -l test.tar.gz
-rw-r--r-- 1 guido guido 2097921 Sep 24 15:51 test.tar.gz
$
首先,我创建了一个 1MiB 的随机数据文件 (a)。然后我将其复制到文件 b,并将其硬链接到 c。在创建 tarball 时,tar 显然知道硬链接,因为 tarball 只有 ~2MiB,而不是 ~3Mib。
现在我希望 gzip 将 tarball 的大小减少到 ~1MiB,因为 a 和 b 是重复的,并且 tarball 内部应该有 1MiB 的连续重复数据,但这并没有发生。
这是为什么?在这种情况下,我该如何有效地压缩 tarball?
答案1
Gzip gzip 基于 DEFLATE 算法,该算法是 LZ77 和 Huffman 编码的组合。它是一种无损数据压缩算法,其工作原理是使用即时构建的字典将输入流转换为压缩符号并监视重复项。但它无法找到相隔超过 32K 的重复项。期望它发现相隔 1MB 的重复项是不现实的。
答案2
妮可·汉密尔顿 (Nicole Hamilton) 正确指出gzip
由于字典大小较小,无法找到远距离重复的数据。
bzip2
类似,因为它的内存限制为 900 KB。
相反,尝试:
LZMA/LZMA2 算法(xz
,7z
)
LZMA 算法与 Deflate 属于同一家族,但使用的字典大小要大得多(可自定义;默认值为 384 MB)。该xz
实用程序应默认安装在大多数最新的 Linux 发行版上,它与 LZMA 类似gzip
并使用 LZMA。
由于 LZMA 可以检测到长距离冗余,因此它能够在此处删除重复数据。不过,它比 Gzip 慢。
另一个选项是 7-zip(软件包7z
中的p7zip
),它是一个存档器(而不是单流压缩器),默认情况下使用 LZMA(由 LZMA 的作者编写)。7-zip 存档器在存档到其.7z
格式时会在文件级别运行自己的重复数据删除(查看具有相同扩展名的文件)。这意味着如果您愿意用 替换tar
,7z
您将获得重复数据删除的相同文件。但是,7z 不会保留纳秒时间戳、权限或 xattrs,因此它可能不适合您的需求。
lrzip
lrzip
是一种压缩器,它会对数据进行预处理,以消除长距离冗余,然后再将其输入到 Gzip/Deflate、bzip2、lzop 或 LZMA 等传统算法中。对于您在此处提供的示例数据,它不是必需的;当输入数据大于内存所能容纳的数据时,它很有用。
对于这种数据(重复的不可压缩块),您应该使用lzop
压缩(非常快)lrzip
,因为一旦重复数据删除,尝试压缩完全随机的数据是没有任何好处的。
布普和奥布南
答案3
gzip
不会找到重复项,即使xz
字典大小很大也不会。你可以做的是使用mksquashfs
- 这确实可以节省重复项的空间。
xz
使用三个随机二进制文件(64MB)进行的一些快速测试结果,mksquashfs
其中两个是相同的:
设置:
mkdir test
cd test
dd if=/dev/urandom of=test1.bin count=64k bs=1k
dd if=/dev/urandom of=test2.bin count=64k bs=1k
cp test{2,3}.bin
cd ..
壁球:
mksquashfs test/ test.squash
> test.squash - 129M
xz:
XZ_OPT='-v --memlimit-compress=6G --memlimit-decompress=512M --lzma2=preset=9e,dict=512M --extreme -T4 ' tar -cJvf test.tar.xz test/
> test.tar.xz - 193M
答案4
作为“机械蜗牛”答案的补充:
如果未压缩的单个文件的大小(或者更准确地说,重复项之间的距离)超出了字典大小,那么即使 xz(或 lzma)也无法找到重复项。即使在最高设置下,xz(或 lzma)也-9e
只为此保留了 64MB。
幸运的是,您可以使用选项指定自己的字典大小--lzma2=dict=256MB
(仅--lzma1=dict=256MB
在使用命令的 lzma 别名时才允许)
不幸的是,当使用自定义压缩链(如上例所示)覆盖设置时,所有其他参数的默认值均未设置为与 -9e 相同的级别。因此单个文件的压缩密度并不高。