为什么Gzip压缩不能消除重复的数据块?


30

我只是做了一个小实验,我用重复的文件创建了一个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的连续数据,但是这没有发生。

为什么是这样?在这些情况下如何有效压缩压缩包?

Answers:


24

Gzip gzip基于DEFLATE算法,该算法是LZ77和Huffman编码的组合。这是一种无损数据压缩算法,其工作原理是使用动态构建的字典将输入流转换为压缩符号,然后注意重复项。但找不到相距超过32K的重复项。期望它发现相距1MB的重复项是不现实的。


很公平!您是否碰巧知道对流不起作用的任何替代方法?
Guido 2012年

1
我不知道您的问题有任何打包的解决方案。如果我预计这将是一个反复出现的严重问题,我(个人)将使用执行n次cmp(比较)操作以查找重复项的脚本来攻击它,将列表写入文件,然后仅使用tar + gzip唯一项+列表。要恢复,我将使用第二个脚本ungzip和untar,然后从列表中创建复制文件。另一个选择是将dups转换为硬链接,因为您知道tar确实可以发现这些链接。抱歉,我知道那可能不是您希望的。
妮可·汉密尔顿

1
由于其设计,gzip和bzip2都必须相对“流友好”-能够作为管道的一部分运行是绝对必要的。您在这里寻找的实际上是重复数据删除,而不仅仅是压缩。由于tar将过程分为两个部分-仅使用tar归档,然后使用第二个程序作为要压缩的过滤器。我在搜索中找不到任何具有重复数据删除功能的压缩存档,但是我发现了之前的相关问题。superuser.com/questions/286414/...
斯蒂芬妮

2
@ Stephanie,NicoleHamilton:有en.wikipedia.org/wiki/Lrzip#Lrzip
机械蜗牛

1
@Guido当然,没有什么可以删除流中不记得的内容的重复项,但是可以尝试类似xz -9 -M 95%,甚至xz -M 95% --lzma2=preset=9,dict=1610612736。速度不会很快,但是您的重复项不太可能留在结果中。
伊罗恩(Eroen)2012年

39

妮可·汉密尔顿(Nicole Hamilton)正确地指出gzip由于字典大小较小,因此找不到远处的重复数据。

bzip2 相似,因为它限制为900 KB的内存。

相反,请尝试:

LZMA / LZMA2算法(xz7z

LZMA算法与Deflate属于同一系列,但使用更大的字典大小(可自定义;默认值为384 MB)。该xz实用程序应默认安装在最新的Linux发行版上,与gzipLZMA 相似并使用LZMA。

由于LZMA检测到远程冗余,因此可以在此处对数据进行重复数据删除。但是,它比Gzip慢。

另一个选项是7-zip(7z位于p7zip软件包中),它是默认情况下使用LZMA的存档器(而不是单流压缩器)(由LZMA的作者编写)。归档到7-zip存档程序的.7z格式时,它会在文件级别运行自己的重复数据删除(查看具有相同扩展名的文件)。这意味着,如果你愿意代替tar7z,你重复数据删除相同的文件。但是,7z不会保留纳秒级的时间戳,权限或xattrs,因此它可能无法满足您的需求。

lrzip

lrzip是一种压缩器,对数据进行预处理以除去长距离冗余,然后再将其馈送到Gzip / Deflate,bzip2,lzop或LZMA等常规算法。对于您在此处提供的示例数据,没有必要;当输入数据大于内存容量时,此功能很有用。

对于此类数据(重复的不可压缩块),应将lzop压缩(非常快)与配合使用lrzip,因为一旦对重复数据进行重复数据删除后再尝试对其进行完全压缩将无济于事。

Bup和Obnam

由于您标记了问题,因此,如果您的目标是备份数据,请考虑使用重复数据删除备份程序,例如BupObnam


这个lrzip看起来很有趣。它甚至有一位以非传统解决方案闻名的作者。现在,我必须修改备份脚本。再次。
Eroen 2012年

3
+1哇,那真是知识/经验的源泉。感激。我可以添加启用了dedup的文件系统吗?ZFS(而且我认为Btrfs计划拥有它)-可以与块对齐的复制一起使用
2012年

使用LZMA2压缩和1536Mb的字典大小(Windows GUI中提供的最大大小)的7Zip非常适合我!
Leopoldo Sanczyk '16

2

如果进行备份(可能包含大量较小的文件),则可能有用的一个技巧是按扩展名对tar中的文件进行排序:

find archive_dir -type f | rev | sort | rev | tar czf my_archive.tar.gz -I -

我剪掉了所有rev的(为什么还要反转然后排序?),然后查看sort选项“ -r,-reverse”(尽管我不确定为什么您还要反转)。但我认为你的tar选择“ -I”不会做你认为它-I, --use-compress-program PROG,你可能想 “-T,--files,从文件”
Xen2050

我相信| tar czf my_archive.tar.gz -I -应该是| xargs tar Azf my_archive.tar.gz
Olivier Dulac

@ Xen2050,rev反转每行中字符的顺序,而不是流中的行顺序。因此,请sort按文件扩展名对文件进行分组。我怀疑-I -应该是-T -,它在stdin上提供了文件列表。
Billyjmc

@billyjmc我看到,rev按扩展名排序,不是说Linux中有很多扩展名。我猜想按大小排序将更有可能找到dup
Xen2050

2

gzip即使xz字典大小很大,也不会找到重复项。您可以做的是使用mksquashfs-确实可以节省重复的空间。

xzmksquashfs带有三个随机二进制文件(64MB)的快速测试结果,其中两个相同:

设定:

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

mksquashfs是否仅在文件级别找到重复项,或者也可以在较小的块上工作?那就是:它还会压缩略有不同但大部分相同的文件吗?
Chaos_99 '16

这仅在基于文件的情况下有效。您可以看到,将这三个测试文件放到非压缩的tar存档中,然后再用mksquashfs压缩它们时,您会看到。另一方面,当在instdout中查找重复项时,mksqashfs将报告Number of duplicate files found
Izzy

1

在我的系统上lzma test.tar产生一个106'3175字节(1.1M)的test.tar.lzma文件


1

作为“机械蜗牛”答案的补充:

如果未压缩的单个文件的文件大小(或更准确地说,重复之间的距离)超过字典大小,则即使xz(或lzma)也不会找到重复项。即使在最高设置-9e下,xz(或lzma)也只能为此保留64MB。

幸运的是,您可以使用选项指定自己的字典大小--lzma2=dict=256MB (仅--lzma1=dict=256MB在对命令使用lzma别名时才允许)

不幸的是,当像上面的示例中那样使用自定义压缩链覆盖设置时,所有其他参数的默认值未设置为与-9e相同的级别。因此,单个文件的压缩密度不是很高。


-2

没有命令行开关的gzip使用最低的算法进行压缩。

尝试使用:

gzip -9 test.tar

你应该得到更好的结果


1
并非如此,差异很小。我也尝试了bzip2,结果相似。
Guido 2012年

没有命令行开关的gzip使用最低的算法进行压缩。=>这不是正确的-“ man gzip”指出“默认压缩级别为-6(也就是说,偏向于高压缩,而牺牲了速度)。” 如果GZIP环境变量未覆盖已编译的默认设置,则对于我知道的所有gzip版本都是如此。正如给定答案中所解释的,即使是“ -9”级也对您没有帮助。
Gunter Ohrner
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.