Perché la compressione Gzip non elimina blocchi di dati duplicati?


30

Ho appena fatto un piccolo esperimento in cui ho creato un archivio tar con file duplicati per vedere se sarebbe stato compresso, con mio stupore, non lo era! Seguono i dettagli (risultati rientrati per piacere di lettura):

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

Innanzitutto ho creato un file 1MiB di dati casuali (a). Quindi l'ho copiato in un file b e l'ho anche collegato a c. Durante la creazione del tarball, tar apparentemente era a conoscenza del collegamento fisico, poiché il tarball era solo ~ 2MiB e non ~ 3Mib.

Ora mi aspettavo che gzip riduca la dimensione del tarball a ~ 1MiB poiché aeb sono duplicati e ci dovrebbero essere 1MiB di dati continui ripetuti all'interno del tarball, ma ciò non è avvenuto.

Perchè è questo? E come potrei comprimere il tarball in modo efficiente in questi casi?

Risposte:


24

Gzip gzip si basa sull'algoritmo DEFLATE, che è una combinazione di LZ77 e codifica Huffman. È un algoritmo di compressione dei dati senza perdita di dati che funziona trasformando il flusso di input in simboli compressi utilizzando un dizionario incorporato al volo e cercando duplicati. Ma non riesce a trovare duplicati separati da più di 32 KB. Aspettarsi di individuare duplicati a 1 MB di distanza non è realistico.


Giusto! Ti capita di conoscere qualche alternativa che non funziona sui flussi?
Guido,

1
Non conosco alcuna soluzione impacchettata al tuo problema. Se mi aspettassi che questo sarebbe un problema ricorrente e serio, lo attaccherei (personalmente) con uno script che eseguiva le operazioni n-way cmp (confronta) per trovare duplicati, scrivere l'elenco in un file, quindi tar + gzip solo il oggetti unici + l'elenco. Per ripristinare, utilizzare un secondo script per decomprimere e decomprimere, quindi creare i duplicati dall'elenco. Un'altra alternativa sarebbe quella di trasformare i duplicati in collegamenti reali, poiché sai che tar li individua. Scusa, so che probabilmente non è quello che speravi.
Nicole Hamilton,

1
gzip e bzip2 devono entrambi essere relativamente "stream friendly" a causa del loro design: è assolutamente necessario poter lavorare come parte di una pipe. Quello che stai cercando qui è in realtà la deduplicazione e non solo la compressione. Poiché tar suddivide il processo in due parti: l'archiviazione solo con tar e quindi l'utilizzo di un secondo programma come filtro per la compressione. Non sono riuscito a trovare alcun archivio compresso con deduplicazione nelle mie ricerche, ma ho trovato questa precedente domanda correlata. superuser.com/questions/286414/…
Stephanie,

2
@Stephanie, NicoleHamilton: C'è en.wikipedia.org/wiki/Lrzip#Lrzip .
Lumaca meccanica,

1
@Guido Ovviamente nulla può rimuovere i duplicati di qualcosa che non ricorda in uno stream, ma prova qualcosa del genere xz -9 -M 95%o addirittura xz -M 95% --lzma2=preset=9,dict=1610612736. Non sarà veloce, ma è improbabile che i tuoi duplicati vengano lasciati nel risultato.
Eroen,

39

Nicole Hamilton osserva correttamente che gzipnon troverà dati duplicati distanti a causa delle sue piccole dimensioni del dizionario.

bzip2 è simile perché è limitato a 900 KB di memoria.

Invece, prova:

Algoritmo LZMA / LZMA2 ( xz, 7z)

L'algoritmo LZMA appartiene alla stessa famiglia di Deflate, ma utilizza una dimensione del dizionario molto più ampia (personalizzabile; l'impostazione predefinita è qualcosa come 384 MB). L' xzutilità, che dovrebbe essere installata per impostazione predefinita sulle distribuzioni Linux più recenti, è simile gzipe utilizza LZMA.

Poiché LZMA rileva ridondanza a lungo raggio, sarà in grado di deduplicare i dati qui. Tuttavia, è più lento di Gzip.

Un'altra opzione è 7-zip ( 7z, nel p7zippacchetto), che è un archiviatore (anziché un compressore a flusso singolo) che utilizza LZMA per impostazione predefinita (scritto dall'autore di LZMA). L'archiviatore a 7 zip esegue la propria deduplicazione a livello di file (guardando i file con la stessa estensione) durante l'archiviazione nel suo .7zformato. Ciò significa che se si desidera sostituire tarcon 7z, si ottengono deduplicati file identici. Tuttavia, 7z non conserva i timestamp, i permessi o gli xattr di nanosecondi, quindi potrebbe non essere adatto alle tue esigenze.

lrzip

lrzipè un compressore che preelabora i dati per rimuovere la ridondanza a lunga distanza prima di inviarli a un algoritmo convenzionale come Gzip / Deflate, bzip2, lzop o LZMA. Per i dati di esempio forniti qui, non è necessario; è utile quando i dati di input sono più grandi di quelli che possono stare in memoria.

Per questo tipo di dati (blocchi duplicati incomprimibili), dovresti usare la lzopcompressione (molto velocemente) con lrzip, perché non c'è alcun vantaggio nel provare di più a comprimere i dati completamente casuali dopo che sono stati deduplicati.

Bup e Obnam

Dato che hai taggato il della domanda , se il tuo obiettivo qui è il backup dei dati, prendi in considerazione l'utilizzo di un programma di backup deduplicato come Bup o Obnam .


Questo lrzip sembra interessante. Ha anche un autore noto per soluzioni non tradizionali. Ora dovrò rivedere i miei script di backup. Ancora.
Eroen,

3
+1 Wow, che fonte di conoscenza / esperienza lì. Apprezzato. Posso aggiungere al mix file system abilitati per la deduplicazione? ZFS (e, penso che Btrfs sia previsto per averlo) - avrebbe funzionato con la duplicazione allineata a blocchi
visto il

7Zip usando la compressione LZMA2 e una dimensione di essiccamento di 1536 Mb (dimensione massima disponibile nella GUI di Windows) funziona alla grande per me!
Leopoldo Sanczyk,

2

Nel caso di un backup, possibilmente con un set ampio di file più piccoli, un trucco che potrebbe funzionare per te è quello di ordinare i file nel tar per estensione:

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

Ritaglierei tutti rev(perché anche invertire e poi ordinare?) E darei un'occhiata sortall'opzione "-r, --reverse" (anche se non sono sicuro del motivo per cui vorresti persino fare qualsiasi inversione). Ma penso che la tua taropzione " -I" non faccia quello che pensi che faccia " -I, --use-compress-program PROG" , probabilmente vuoi "-T, --files-from FILE"
Xen2050,

Credo che | tar czf my_archive.tar.gz -I -dovrebbe essere| xargs tar Azf my_archive.tar.gz
Olivier Dulac il

@ Xen2050, revinverte l'ordine dei caratteri in ciascuna riga, non l'ordine delle righe nello stream. Per questo sortmotivo , raggruppa i file in base all'estensione. Ho il sospetto che -I -avrebbe dovuto essere -T -, che fornisce l'elenco dei file su stdin.
Billyjmc,

@billyjmc Vedo, che revsarebbe un po 'organizzato per estensione, non che ci siano comunque molte estensioni in Linux. Immagino che l'ordinamento per dimensione avrebbe maggiori possibilità di trovare
Dup

2

gzipnon troverà duplicati, anche xzcon una dimensione del dizionario enorme non lo farà. Quello che puoi fare è usare mksquashfs- questo risparmierà davvero lo spazio dei duplicati.

Alcuni risultati di test rapidi con xze mksquashfscon tre file binari casuali (64 MB) di cui due uguali:

Impostare:

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

Mksquashfs trova solo duplicati a livello di file o funziona anche su blocchi più piccoli? Cioè: comprimerà anche file leggermente diversi ma principalmente gli stessi?
Chaos_99

Questo funziona solo su base file. Puoi vederlo quando esegui il taring di questi tre file di test in un archivio tar non compresso e li comprimi successivamente con mksquashfs. D'altra parte, mksqashfs riporterà, quando trova duplicati con Number of duplicate files foundin stdout.
Izzy,

1

Sul mio sistema lzma test.tarrisulta un file test.tar.lzma di 106'3175 byte (1.1M)


1

In aggiunta alla risposta della 'lumaca meccanica:

Anche xz (o lzma) non troverà duplicati se la dimensione del file singolo non compresso (o, più precisamente, la distanza tra i duplicati) supera la dimensione del dizionario. xz (o lzma) anche con l'impostazione più alta -9eriserva solo 64 MB per questo.

Fortunatamente puoi specificare la tua dimensione dittonaria con l'opzione --lzma2=dict=256MB ( --lzma1=dict=256MBè consentito solo quando si usa l'alias lzma per il comando)

Sfortunatamente, quando si sovrascrivono le impostazioni con catene di compressione personalizzate come indicato nell'esempio sopra, i valori predefiniti per tutti gli altri parametri non sono impostati allo stesso livello di -9e. Quindi la densità di compressione non è così alta per i singoli file.


-2

gzip senza opzioni della riga di comando utilizza l'algoritmo più basso possibile per la compressione.

Prova a usare:

gzip -9 test.tar

Dovresti ottenere risultati migliori


1
Non proprio, la differenza è minima. Ho anche provato bzip2 con risultati simili.
Guido,

gzip senza opzioni della riga di comando utilizza l'algoritmo più basso possibile per la compressione. => Questo non è vero - "man gzip" afferma che "(t) il suo livello di compressione predefinito è -6 (cioè distorto verso una compressione elevata a scapito della velocità)." Questo è vero per tutte le versioni di gzip che conosco, se le impostazioni predefinite compilate non vengono sovrascritte dalla variabile d'ambiente GZIP. Anche il livello "-9" non ti aiuterà qui, come già spiegato nelle risposte fornite.
Gunter Ohrner,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.