Por que a compactação Gzip não elimina blocos duplicados de dados?

30

Acabei de fazer um pequeno experimento em que criei um arquivo tar com arquivos duplicados para ver se ele seria compactado, para minha admiração, não era! Detalhes a seguir (resultados recuados para o prazer de ler):

$ 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
$ 

Primeiro, criei um arquivo de 1MiB de dados aleatórios (a). Depois copiei para um arquivo be também vinculei-o a c. Ao criar o tarball, o tar aparentemente estava ciente do hardlink, já que o tarball tinha apenas ~ 2MiB e não ~ 3Mib.

Agora eu esperava que o gzip reduzisse o tamanho do tarball para ~ 1MiB, já que aeb são duplicados, e deveria haver 1MiB de dados contínuos repetidos dentro do tarball, mas isso não ocorreu.

Por que é isso? E como eu poderia comprimir o tarball com eficiência nesses casos?

Guido
fonte

Respostas:

24

Gzip O gzip é baseado no algoritmo DEFLATE, que é uma combinação da codificação LZ77 e Huffman. É um algoritmo de compactação de dados sem perdas que funciona transformando o fluxo de entrada em símbolos compactados usando um dicionário construído on-the-fly e procurando duplicatas. Mas não consegue encontrar duplicatas separadas por mais de 32K. Esperar que ele localize duplicatas com 1 MB de distância não é realista.

Nicole Hamilton
fonte
Justo! Você conhece alguma alternativa que não funcione em fluxos?
Guido
1
Não conheço nenhuma solução empacotada para o seu problema. Se eu esperasse que isso fosse um problema sério e recorrente, eu (pessoalmente) o atacaria com um script que executasse as operações n-way cmp (compare) para encontrar duplicatas, escrever a lista em um arquivo e tar + gzip apenas o itens exclusivos + a lista. Para restaurar, eu usaria um segundo script para descompactar e descompactar e, em seguida, criar os dups da lista. Outra alternativa seria transformar os dups em links físicos, já que você sabe que o tar os identifica. Desculpe, eu sei que provavelmente não é o que você estava esperando.
22612 Nicole Hamilton
1
O gzip e o bzip2 precisam ser relativamente "amigáveis ​​ao fluxo" devido ao seu design - é absolutamente necessário poder trabalhar como parte de um pipe. O que você está procurando aqui é na verdade deduplicação e não apenas compactação. Como o tar divide o processo em duas partes - arquivando apenas com tar e, em seguida, usando um segundo programa como filtro para compactar. Não encontrei nenhum arquivo compactado com desduplicação em minhas pesquisas, mas encontrei esta pergunta relacionada anterior. superuser.com/questions/286414/…
Stephanie
2
@ Stephanie, NicoleHamilton: Existe en.wikipedia.org/wiki/Lrzip#Lrzip .
Caracol mecânico
1
@Guido É claro que nada pode remover duplicatas de algo que não se lembra em um fluxo, mas tente algo como xz -9 -M 95%, ou mesmo xz -M 95% --lzma2=preset=9,dict=1610612736. Não será rápido, mas é improvável que suas duplicatas sejam deixadas no resultado.
Eroen
39

Nicole Hamilton nota corretamente que gzipnão encontrará dados duplicados distantes devido ao seu pequeno tamanho de dicionário.

bzip2 é semelhante, porque está limitado a 900 KB de memória.

Em vez disso, tente:

Algoritmo LZMA / LZMA2 ( xz, 7z)

O algoritmo LZMA está na mesma família que Deflate, mas usa um tamanho de dicionário muito maior (personalizável; o padrão é algo como 384 MB). O xzutilitário, que deve ser instalado por padrão nas distribuições Linux mais recentes, é semelhante ao gzipe usa LZMA.

Como o LZMA detecta redundância de longo alcance, ele pode deduplicar seus dados aqui. No entanto, é mais lento que o Gzip.

Outra opção é o 7-zip ( 7z, no p7zippacote), que é um arquivador (em vez de um compressor de fluxo único) que usa o LZMA por padrão (escrito pelo autor do LZMA). O arquivador 7-zip executa sua própria desduplicação no nível do arquivo (observando arquivos com a mesma extensão) ao arquivar em seu .7zformato. Isto significa que se você está disposto a substituir tarcom 7z, você tem arquivos idênticos desduplicados. No entanto, o 7z não preserva timestamps, permissões ou xattrs em nanossegundos, portanto, pode não atender às suas necessidades.

lrzip

lrzipé um compressor que processa previamente os dados para remover a redundância de longa distância antes de alimentá-los com um algoritmo convencional como Gzip / Deflate, bzip2, lzop ou LZMA. Para os dados de amostra que você fornece aqui, não é necessário; é útil quando os dados de entrada são maiores do que o que pode caber na memória.

Para esse tipo de dados (blocos incompressíveis duplicados), você deve usar a lzopcompactação (muito rápido) com lrzip, porque não há nenhum benefício em se esforçar mais para compactar dados completamente aleatórios depois de deduplicados.

Bup e Obnam

Como você marcou o da pergunta , se seu objetivo aqui é fazer backup de dados, considere usar um programa de backup com desduplicação como Bup ou Obnam .

Caracol mecânico
fonte
Este lrzip parece interessante. Ele ainda tem um autor conhecido por soluções não tradicionais. Agora vou ter que revisar meus scripts de backup. Novamente.
Eroen
3
+1 Uau, que fonte de conhecimento / experiência lá. Estimado. Posso adicionar sistemas de arquivos habilitados para desduplicação à mistura? ZFS (e, eu acho Btrfs está programado para tê-lo) - iria trabalhar com o bloco alinhado duplicação
sehe
7Zip usando compactação LZMA2 e um tamanho diccionário de 1536Mb (tamanho máximo disponível na GUI do Windows) funciona muito bem para mim!
Leopoldo Sanczyk 2/11
2

No caso de um backup, possivelmente com um amplo conjunto de arquivos menores, um truque que pode funcionar para você é classificar os arquivos no tar por extensão:

find archive_dir -type f | rev | sort | rev | tar czf my_archive.tar.gz -I -
user216110
fonte
Eu recortava todasrev as sortopções (por que reverter e depois classificaria?) E olhava para a opção "-r, --reverse" (embora eu não tenha certeza do porquê de querer inversão). Mas acho que sua taropção " -I" não faz o que você pensa que faz " -I, --use-compress-program PROG" , você provavelmente deseja "-T, --files-from FILE"
Xen2050 02/02/2015
Eu acredito que | tar czf my_archive.tar.gz -I -deveria ser| xargs tar Azf my_archive.tar.gz
Olivier Dulac
@ Xen2050, revinverte a ordem dos caracteres em cada linha, não a ordem das linhas no fluxo. Por esse sortmotivo , agrupa os arquivos por sua extensão. Eu suspeito que -I -deveria ter sido -T -, o que fornece a lista de arquivos no stdin.
billyjmc
@billyjmc eu vejo, isso revmeio que arranja por extensão, não que haja muitas extensões no linux de qualquer maneira. Eu imagino a classificação por tamanho teria uma chance maior de encontrar dup de
Xen2050
2

gzipnão encontrará duplicatas, mesmo xzcom um tamanho enorme de dicionário. O que você pode fazer é usar mksquashfs- isso economizará o espaço de duplicatas.

Alguns resultados rápidos de testes com xze mksquashfscom três arquivos binários aleatórios (64 MB), dos quais dois são iguais:

Configuração:

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 ..

Squashfs:

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
Izzy
fonte
O mksquashfs encontra apenas duplicatas no nível do arquivo ou também funciona em pedaços menores? Ou seja: Ele também comprimirá arquivos ligeiramente diferentes, mas a maioria são os mesmos?
Chaos_99
Isso funciona apenas em uma base de arquivo. Você pode ver isso ao tarar esses três arquivos de teste em um arquivo tar não compactado e compactá-los com o mksquashfs posteriormente. Por outro lado, o mksqashfs reportará ao encontrar duplicatas com Number of duplicate files foundno stdout.
Izzy
1

No meu sistema, lzma test.tarresulta em um arquivo test.tar.lzma de 106'3175 bytes (1,1M)

rmweiss
fonte
1

Como complemento à resposta do 'caracol mecânico:

Mesmo xz (ou lzma) não encontrará duplicatas se o tamanho do arquivo único não compactado (ou, mais precisamente, a distância entre as duplicatas) exceder o tamanho do dicionário. xz (ou lzma), mesmo na configuração mais alta, -9ereserva apenas 64 MB para isso.

Felizmente, você pode especificar seu próprio tamanho dictonário com a opção --lzma2=dict=256MB (somente --lzma1=dict=256MBé permitida ao usar o alias lzma no comando)

Infelizmente, ao substituir as configurações por cadeias de compactação personalizadas, como fornecido no exemplo acima, os valores padrão para todos os outros parâmetros não são definidos no mesmo nível que com -9e. Portanto, a densidade de compactação não é tão alta para arquivos únicos.

Chaos_99
fonte
-2

O gzip sem opções de linha de comando usa o algoritmo mais baixo possível para compactação.

Tente usar:

gzip -9 test.tar

Você deve obter melhores resultados

J Baron
fonte
1
Na verdade, a diferença é mínima. Eu também tentei o bzip2 com resultados semelhantes.
Guido
O gzip sem opções de linha de comando usa o algoritmo mais baixo possível para compactação. => Isso não é verdade - "man gzip" afirma que "(t) o nível de compactação padrão é -6 (ou seja, inclinado para alta compactação em detrimento da velocidade)." Isso é verdade para todas as versões do gzip que eu conheço, se as configurações padrão compiladas não forem substituídas pela variável de ambiente GZIP. Mesmo o nível "-9" não o ajudará aqui, como já explicado nas respostas fornecidas.
Gunter Ohrner