Я только что провел небольшой эксперимент, создав архив 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 МБ (a). Затем я скопировал его в файл b и также связал его с c. При создании tarball tar, по-видимому, знал о жесткой ссылке, поскольку tarball был всего ~2 МБ, а не ~3 МБ.
Я ожидал, что gzip уменьшит размер tarball примерно до 1 МБ, поскольку a и b являются дубликатами, а внутри tarball должен быть 1 МБ непрерывных повторяющихся данных, но этого не произошло.
Почему это так? И как мне эффективно сжать tarball в этих случаях?
решение1
Gzip gzip основан на алгоритме DEFLATE, который представляет собой комбинацию кодирования LZ77 и Хаффмана. Это алгоритм сжатия данных без потерь, который преобразует входной поток в сжатые символы с помощью словаря, созданного на лету, и отслеживает дубликаты. Но он не может найти дубликаты, разделенные более чем 32 КБ. Ожидать, что он обнаружит дубликаты, разделенные 1 МБ, нереально.
решение2
Николь Гамильтон правильно отмечаеткоторый 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
, поскольку нет смысла пытаться сильнее сжать полностью случайные данные после того, как они были дедуплицированы.
Буп и Обнам
Поскольку вы отметили вопросрезервное копированиеЕсли вашей целью является резервное копирование данных, рассмотрите возможность использования дедуплицирующей программы резервного копирования, напримерБапилиОбнам.
решение3
gzip
не найдет дубликатов, даже xz
при огромном размере словаря не найдет. Что вы можете сделать, так это использовать mksquashfs
- это действительно сэкономит место дубликатов.
Некоторые быстрые результаты тестирования с xz
тремя mksquashfs
случайными двоичными файлами (64 МБ), два из которых одинаковы:
Настраивать:
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_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
решение4
В дополнение к ответу «механической улитки»:
Даже xz (или lzma) не найдет дубликаты, если размер несжатого отдельного файла (или, точнее, расстояние между дубликатами) превышает размер словаря. xz (или lzma) даже на самых высоких настройках -9e
резервирует для этого только 64 МБ.
К счастью, вы можете указать свой собственный размер словаря с помощью опции --lzma2=dict=256MB
( --lzma1=dict=256MB
это разрешено только при использовании псевдонима lzma для команды)
К сожалению, при переопределении настроек с помощью пользовательских цепочек сжатия, как в примере выше, значения по умолчанию для всех остальных параметров не устанавливаются на том же уровне, что и при -9e. Поэтому плотность сжатия не такая высокая для отдельных файлов.