Pourquoi la compression Gzip n'élimine-t-elle pas les blocs de données en double?

30

Je viens de faire une petite expérience où j'ai créé une archive tar avec des fichiers en double pour voir si elle serait compressée, à ma grande admiration, ce n'était pas le cas! Les détails suivent (résultats en retrait pour le plaisir de lire):

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

J'ai d'abord créé un fichier 1 Mo de données aléatoires (a). Ensuite, je l'ai copié dans un fichier b et l'ai également lié à c. Lors de la création de l'archive tar, tar était apparemment au courant du lien dur, car l'archive tar n'était que de ~ 2 Mo et non de ~ 3Mib.

Maintenant, je m'attendais à ce que gzip réduise la taille de l'archive tar à ~ 1 Mo, car a et b sont des doublons, et il devrait y avoir 1 Mo de données continues répétées à l'intérieur de l'archive, mais cela ne s'est pas produit.

Pourquoi est-ce? Et comment pourrais-je compresser efficacement l'archive tar dans ces cas?

Guido
la source

Réponses:

24

Gzip gzip est basé sur l'algorithme DEFLATE, qui est une combinaison du codage LZ77 et Huffman. Il s'agit d'un algorithme de compression de données sans perte qui fonctionne en transformant le flux d'entrée en symboles compressés à l'aide d'un dictionnaire créé à la volée et en surveillant les doublons. Mais il ne peut pas trouver de doublons séparés par plus de 32 Ko. Il n'est pas réaliste de s'attendre à repérer des doublons à 1 Mo d'intervalle.

Nicole Hamilton
la source
C'est suffisant! Connaissez-vous une alternative qui ne fonctionne pas sur les flux?
Guido
1
Je ne connais aucune solution intégrée à votre problème. Si je m'attendais à ce que ce soit un problème grave et récurrent, je l'attaquerais (personnellement) avec un script qui a effectué les opérations cmp (comparer) à n pour trouver des doublons, écrire la liste dans un fichier, puis tar + gzip uniquement le articles uniques + la liste. Pour restaurer, j'utiliserais un deuxième script pour décompresser et décompresser, puis créer les doublons à partir de la liste. Une autre alternative serait de transformer les doublons en liens durs, car vous savez que tar les repère. Désolé, je sais que ce n'est probablement pas ce que vous espériez.
Nicole Hamilton
1
gzip et bzip2 doivent tous deux être relativement "stream friendly" en raison de leur conception - il est absolument nécessaire de pouvoir travailler dans le cadre d'un pipe. Ce que vous recherchez ici est en fait la déduplication et pas seulement la compression. Étant donné que tar divise le processus en deux parties - l'archivage uniquement avec tar, puis l'utilisation d'un deuxième programme comme filtre pour compresser. Je n'ai trouvé aucune archive compressée avec déduplication dans mes recherches, mais j'ai trouvé cette question connexe précédente. superuser.com/questions/286414/…
Stephanie
2
@Stephanie, NicoleHamilton: Il y a en.wikipedia.org/wiki/Lrzip#Lrzip .
Escargot mécanique
1
@Guido Bien sûr, rien ne peut supprimer les doublons de quelque chose dont il ne se souvient pas dans un flux, mais essayez quelque chose comme xz -9 -M 95%, ou même xz -M 95% --lzma2=preset=9,dict=1610612736. Ce ne sera pas rapide, mais vos doublons ne resteront probablement pas dans le résultat.
Eroen
39

Nicole Hamilton note correctement que gzipne trouvera pas de données en double éloignées en raison de la petite taille de son dictionnaire.

bzip2 est similaire, car il est limité à 900 Ko de mémoire.

Essayez plutôt:

Algorithme LZMA / LZMA2 ( xz, 7z)

L'algorithme LZMA appartient à la même famille que Deflate, mais utilise une taille de dictionnaire beaucoup plus grande (personnalisable; la valeur par défaut est d'environ 384 Mo). L' xzutilitaire, qui devrait être installé par défaut sur les distributions Linux les plus récentes, est similaire gzipet utilise LZMA.

Comme LZMA détecte une redondance à plus longue portée, il pourra dédupliquer vos données ici. Cependant, il est plus lent que Gzip.

Une autre option est 7-zip ( 7z, dans le p7zippackage), qui est un archiveur (plutôt qu'un compresseur à flux unique) qui utilise LZMA par défaut (écrit par l'auteur de LZMA). L'archiveur 7-zip exécute sa propre déduplication au niveau du fichier (en regardant les fichiers avec la même extension) lors de l'archivage à son .7zformat. Cela signifie que si vous êtes prêt à remplacer taravec 7z, vous obtenez des fichiers identiques dédupliquées. Cependant, 7z ne conserve pas les horodatages, les autorisations ou les xattrs en nanosecondes, il peut donc ne pas répondre à vos besoins.

lrzip

lrzipest un compresseur qui pré-traite les données pour supprimer la redondance longue distance avant de les alimenter à un algorithme conventionnel comme Gzip / Deflate, bzip2, lzop ou LZMA. Pour les exemples de données que vous donnez ici, ce n'est pas nécessaire; il est utile lorsque les données d'entrée sont plus grandes que ce qui peut tenir en mémoire.

Pour ce type de données (morceaux incompressibles dupliqués), vous devez utiliser la lzopcompression (très rapide) avec lrzip, car il n'y a aucun avantage à essayer plus fort de compresser des données complètement aléatoires une fois qu'elles sont dédupliquées.

Bup et Obnam

Depuis que vous avez marqué la la question , si votre objectif ici est de sauvegarder des données, envisagez d'utiliser un programme de sauvegarde avec déduplication comme Bup ou Obnam .

Escargot mécanique
la source
Ce lrzip semble intéressant. Il a même un auteur connu pour ses solutions non traditionnelles. Je vais maintenant devoir réviser mes scripts de sauvegarde. Encore.
Eroen
3
+1 Wow, quelle fontaine de connaissances / d'expérience là-bas. J'ai apprécié. Puis-je ajouter des systèmes de fichiers avec déduplication au mixage? ZFS (et, je pense que Btrfs est prévu pour l' avoir) - travaillerait avec duplication aligné bloc
sehe
7Zip utilisant la compression LZMA2 et une taille dédiée de 1536 Mo (taille maximale disponible dans l'interface graphique Windows) fonctionne très bien pour moi!
Leopoldo Sanczyk
2

Dans le cas d'une sauvegarde, éventuellement avec un ensemble plus large de fichiers plus petits, une astuce qui pourrait fonctionner pour vous est de trier les fichiers dans le tar par extension:

find archive_dir -type f | rev | sort | rev | tar czf my_archive.tar.gz -I -
user216110
la source
Je couperais tous les rev(pourquoi même inverser puis trier?) Et regarder l' sortoption "-r, --reverse" (même si je ne sais pas pourquoi vous voudriez même inverser). Mais je pense que votre taroption " -I" ne fait pas ce que vous pensez " -I, --use-compress-program PROG" , vous voulez probablement "-T, --files-from FILE"
Xen2050
Je crois que ça | tar czf my_archive.tar.gz -I -devrait être| xargs tar Azf my_archive.tar.gz
Olivier Dulac
@ Xen2050, revinverse l'ordre des caractères dans chaque ligne, pas l'ordre des lignes dans le flux. Pour cette raison, sortregroupe les fichiers par leur extension. Je soupçonne que le -I -devrait avoir été -T -, qui fournit la liste des fichiers sur stdin.
billyjmc
@billyjmc Je vois, ce revserait en quelque sorte arrangé par extension, pas qu'il y ait de nombreuses extensions sous Linux de toute façon. J'imagine que le tri par taille aurait plus de chances de trouver des dup
Xen2050
2

gzipne trouvera pas de doublons, même xzavec une énorme taille de dictionnaire. Ce que vous pouvez faire, c'est utiliser mksquashfs- cela permettra en effet d'économiser l'espace des doublons.

Quelques résultats de tests rapides avec xzet mksquashfsavec trois fichiers binaires aléatoires (64 Mo) dont deux identiques:

Installer:

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
la source
Est-ce que mksquashfs ne trouve que des doublons au niveau du fichier, ou fonctionne-t-il également sur des morceaux plus petits? C'est-à-dire: compressera-t-il également des fichiers légèrement différents, mais principalement les mêmes?
Chaos_99
Cela fonctionne afaik sur une base de fichiers uniquement. Vous pouvez voir cela lors du tarage de ces trois fichiers de test dans une archive tar non compressée et de les compresser avec mksquashfs par la suite. D'un autre côté, mksqashfs rendra compte lors de la recherche de doublons avec Number of duplicate files foundin stdout.
Izzy
1

Sur mon système, le lzma test.tarfichier test.tar.lzma de 106'3175 octets (1,1 Mo)

rmweiss
la source
1

En complément de la réponse de l'escargot mécanique:

Même xz (ou lzma) ne trouvera pas de doublons si la taille du fichier unique non compressé (ou, plus précisément, la distance entre les doublons) dépasse la taille du dictionnaire. xz (ou lzma) même sur le réglage le plus élevé -9ene réserve que 64 Mo pour cela.

Heureusement, vous pouvez spécifier votre propre taille de dicton avec l'option --lzma2=dict=256MB (uniquement --lzma1=dict=256MBautorisé lorsque vous utilisez l'alias lzma pour la commande)

Malheureusement, lors de la substitution des paramètres avec des chaînes de compression personnalisées comme indiqué dans l'exemple ci-dessus, les valeurs par défaut pour tous les autres paramètres ne sont pas définies au même niveau qu'avec -9e. La densité de compression n'est donc pas aussi élevée pour des fichiers uniques.

Chaos_99
la source
-2

gzip sans commutateurs de ligne de commande utilise l'algorithme le plus bas possible pour la compression.

Essayez d'utiliser:

gzip -9 test.tar

Vous devriez obtenir de meilleurs résultats

J Baron
la source
1
Pas vraiment, la différence est minime. J'ai également essayé bzip2 avec des résultats similaires.
Guido
gzip sans commutateurs de ligne de commande utilise l'algorithme le plus bas possible pour la compression. => Ce n'est pas vrai - "man gzip" déclare que "(t) le niveau de compression par défaut est -6 (c'est-à-dire, biaisé vers une compression élevée au détriment de la vitesse)." Cela est vrai pour toutes les versions de gzip que je connais, si les paramètres par défaut compilés ne sont pas remplacés par la variable d'environnement GZIP. Même le niveau "-9" ne vous aidera pas ici, comme déjà expliqué dans les réponses données.
Gunter Ohrner