Почему сжатие 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
$ 

Сначала я создал файл случайных данных размером 1 МБ (а). Затем я скопировал его в файл b и также связал его с c. При создании тарбола tar явно знал о жесткой связи, поскольку тарбол был всего ~ 2MiB, а не ~ 3Mib.

Теперь я ожидал, что gzip уменьшит размер архива до ~ 1 МБ, так как a и b являются дубликатами, и внутри архива должно быть 1 МБ непрерывных данных, но этого не произошло.

Почему это? И как мне эффективно сжать тарбол в этих случаях?

Guido
источник

Ответы:

24

Gzip gzip основан на алгоритме DEFLATE, который представляет собой комбинацию кодирования LZ77 и Хаффмана. Это алгоритм сжатия данных без потерь, который работает путем преобразования входного потока в сжатые символы, используя словарь, созданный на лету, и отслеживая дубликаты. Но он не может найти дубликаты, разделенные более чем 32K. Ожидать, что он обнаружит дубликаты на расстоянии 1 МБ, нереально.

Николь Гамильтон
источник
Справедливо! Вы случайно не знаете альтернативу, которая не работает на потоках?
Гвидо
1
Я не знаю ни одного упакованного решения вашей проблемы. Если бы я ожидал, что это будет повторяющаяся серьезная проблема, я (лично) напал бы на нее с помощью скрипта, который выполнял n-way cmp (сравнение) операций, чтобы найти дубликаты, записать список в файл, затем tar + gzip только уникальные предметы + список. Чтобы восстановить, я бы использовал второй скрипт, чтобы разархивировать и распаковать, а затем создать дупс из списка. Другой альтернативой может стать превращение дупов в жесткие ссылки, так как вы знаете, что 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. Это не будет быстро, но ваши дубликаты вряд ли останутся в результате.
Эроен
39

Николь Хэмилтон правильно отмечает, что gzipне найдет отдаленные дубликаты данных из-за небольшого размера словаря

bzip2 похоже, потому что он ограничен 900 КБ памяти.

Вместо этого попробуйте:

Алгоритм LZMA / LZMA2 ( xz, 7z)

Алгоритм LZMA принадлежит тому же семейству, что и Deflate, но использует гораздо больший размер словаря (настраивается; по умолчанию это что-то вроде 384 МБ). xzУтилита, которая должна быть установлена по умолчанию в большинстве последних дистрибутивов Linux, аналогична 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, поскольку нет смысла пытаться сложнее сжимать полностью случайные данные после их дедупликации.

Буп и Обнам

Так как вы помечены на вопрос , если ваша цель здесь резервное копирование данных, рассмотрите возможность использования дедуплицирующей программы резервной копирования , как БУП или Obnam .

Механическая улитка
источник
Этот лрзип выглядит интересно. У него даже есть автор, известный нетрадиционными решениями. Теперь мне придется пересмотреть мои резервные скрипты. Опять таки.
Эроен
3
+1 Вау, какой там источник знаний / опыта. Оценил. Могу ли я добавить в микс файловые системы с поддержкой дедупликации? ZFS (и, я думаю, Btrfs должен его иметь) - будет работать с дублированием с выравниванием по
блокам
7Zip с использованием сжатия LZMA2 и дирекционного размера 1536 Мб (максимальный размер, доступный в графическом интерфейсе Windows) отлично работает для меня!
Леопольдо Санчик
2

В случае резервного копирования, возможно, с большим набором файлов меньшего размера, одна хитрость, которая может работать для вас, заключается в сортировке файлов в tar по расширению:

find archive_dir -type f | rev | sort | rev | tar czf my_archive.tar.gz -I -
user216110
источник
Я бы вырезал все rev(почему даже перевернуть, а затем отсортировать?) И посмотреть на sortопцию «-r, --reverse» (хотя я не уверен, почему вы даже хотите перевернуть). Но я думаю, что ваш tarвариант " -I" не делает то, что вы думаете " -I, --use-compress-program PROG" , вы, вероятно, хотите "-T, --files-from FILE"
Xen2050
Я считаю, что | tar czf my_archive.tar.gz -I -должно быть| xargs tar Azf my_archive.tar.gz
Оливье Дюлак
@ Xen2050, revменяет порядок символов в каждой строке, а не порядок строк в потоке. Из-за этого sortгруппирует файлы по их расширению. Я подозреваю, что -I -должен был быть -T -, который предоставляет список файлов на стандартный ввод.
Billyjmc
@billyjmc Я вижу, это revбыло бы как-то упорядочено по расширению, не то чтобы в linux было много расширений. Я предполагаю, что сортировка по размеру будет иметь больше шансов найти
дупла
2

gzipне найдет дубликатов, даже xzс огромным размером словаря. То, что вы можете сделать, это использовать mksquashfs- это действительно сэкономит пространство дубликатов.

Некоторые быстрые результаты испытаний с xzи mksquashfsс тремя случайными двоичными файлами (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 ..

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 дубликаты только на уровне файлов, или он также работает на небольших кусках? То есть: он также сжимает немного разные, но в основном те же файлы?
Chaos_99
Это работает только на файловой основе. Вы можете видеть это при переносе этих трех тестовых файлов в несжатый архив tar и последующем сжатии их с помощью mksquashfs. С другой стороны, mksqashfs сообщит, когда найдет дубликаты Number of duplicate files foundв stdout.
Иззи
1

В моей системе lzma test.tarполучается файл test.tar.lzma размером 106'3175 байт (1.1M)

rmweiss
источник
1

Как дополнение к ответу механической улитки:

Даже xz (или lzma) не найдет дубликаты, если размер несжатого отдельного файла (или, точнее, расстояние между дубликатами) превышает размер словаря. xz (или lzma) даже при самых высоких настройках -9eрезервирует для этого только 64 МБ.

К счастью, вы можете указать свой собственный размер диктонары с помощью опции --lzma2=dict=256MB ( --lzma1=dict=256MBдопускается только при использовании псевдонима lzma в команде)

К сожалению, при переопределении настроек с помощью пользовательских цепочек сжатия, как указано в примере выше, значения по умолчанию для всех остальных параметров не устанавливаются на тот же уровень, что и с -9e. Таким образом, плотность сжатия не так высока для отдельных файлов.

Chaos_99
источник
-2

В gzip без ключей командной строки используется минимально возможный алгоритм сжатия.

Попробуйте использовать:

gzip -9 test.tar

Вы должны получить лучшие результаты

Дж Барон
источник
1
Не совсем, разница минимальная. Я также попробовал bzip2 с похожими результатами.
Гвидо
В gzip без ключей командной строки используется минимально возможный алгоритм сжатия. => Это не так - «man gzip» заявляет, что «(t) уровень сжатия по умолчанию равен -6 (то есть смещен в сторону высокого сжатия за счет скорости)». Это верно для всех известных мне версий gzip, если скомпилированные настройки по умолчанию не переопределяются переменной среды GZIP. Даже уровень "-9" здесь вам не поможет, как уже объяснялось в приведенных ответах.
Гюнтер Орнер