Чому стиснення Gzip не усуває повторюваних фрагментів даних?


30

Я просто зробив невеликий експеримент, де створив архів дьогтю з дублікатами файлів, щоб побачити, чи не стиснеться він, на превеликий жаль, це не так! Деталі наступні (результати з відступом для задоволення від читання):

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

Спочатку я створив 1MiB файл випадкових даних (a). Потім я скопіював його у файл b, а також з'єднав його з c. Створюючи тарбол, смола, мабуть, знала про жорстке посилання, оскільки тарбол був лише ~ 2MiB, а не ~ 3Mib.

Тепер я очікував, що gzip зменшить розмір тарболу до ~ 1MiB, оскільки a і b є дублікатами, і має бути 1MiB безперервних даних, що повторюються всередині тарболу, але цього не сталося.

Чому це? І як я міг би ефективно стискати тарбол у цих випадках?

Відповіді:


24

Gzip gzip заснований на алгоритмі DEFLATE, який є комбінацією кодування LZ77 та Хаффмана. Це алгоритм стиснення даних без втрат, який працює, трансформуючи вхідний потік у стислі символи, використовуючи словник, побудований під час руху та перегляд дублікатів. Але він не може знайти дублікати, розділені на більш ніж 32 К. Очікувати, що він помітить копії 1 Мб один від одного, не реально.


Досить справедливо! Чи трапляється вам відомо про будь-яку альтернативу, яка не працює на потоках?
Гвідо

1
Я не знаю жодного пакетованого рішення вашої проблеми. Якби я очікував, що це буде повторювана, серйозна проблема, я (особисто) атакував би його сценарієм, який робив операції n-way cmp (зіставлення), щоб знайти дублікати, записати список у файл, а потім tar + gzip тільки унікальні предмети + список. Щоб відновити, я використовував би другий скрипт, щоб скасувати і untar, а потім створити дупи зі списку. Іншою альтернативою може стати перетворення дуппів на жорсткі зв’язки, оскільки ви знаєте, що дьоготь їх помічає. Вибачте, я знаю, що це, мабуть, не те, на що ви сподівалися.
Ніколь Гамільтон

1
gzip і bzip2 повинні бути відносно "сприятливими для потоку" через їх дизайн - це абсолютно необхідно, щоб мати можливість працювати як частина труби. Що ви шукаєте тут, це насправді дедуплікація, а не просто стиснення. Оскільки дьоготь розбиває процес на дві частини - архівування лише за допомогою дьогтю, а потім використання другої програми як фільтра для стиснення. Я не міг знайти жодного стислого архіву з дедуплікацією під час пошуку, але я знайшов це попереднє пов'язане питання. superuser.com/questions/286414/…
Стефанія


1
@Guido Звичайно, ніщо не може видалити дублікати чогось, чого він не пам’ятає, в потоці, але спробуйте щось на кшталт xz -9 -M 95%або навіть xz -M 95% --lzma2=preset=9,dict=1610612736. Це не буде швидко, але ваші дублікати навряд чи залишаться в результаті.
Eroen

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, оскільки немає ніякої користі намагатися складніше стискати цілком випадкові дані після їх дублювання.

Буп і Обнам

Оскільки ви позначили запитань , якщо ваша мета - це резервне копіювання даних, спробуйте скористатись програмою резервного копіювання, наприклад, Bup чи Obnam .


Цей lrzip виглядає цікаво. У нього навіть є автор, відомий нетрадиційними рішеннями. Тепер мені доведеться переглянути свої резервні сценарії. Знову.
Eroen

3
+1 Вау, який джерело знань / досвіду там. Вдячний. Чи можу я додати в суміш файлові системи, що підтримують дедупцію? ZFS (і, я думаю, Btrfs запланований, щоб його мати) - працював би з дублюванням блоків, вирівнювання
1212

7Zip, що використовує стиснення LZMA2 та розмір дикторію 1536 Мбіт (максимальний розмір, доступний у графічному інтерфейсі Windows), для мене чудово підходить!
Леопольдо Санчик

2

У разі створення резервної копії, можливо, з великим набором менших файлів, один фокус, який може працювати для вас, - це сортування файлів у тарі за розширенням:

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

Я вирізав би всі rev"(чому навіть зворотний, а потім сортувати?) І поглянув на sortопцію " -r, --reverse " (хоча я не впевнений, чому ви хотіли б навіть реверсу). Але я думаю , що ваш tarваріант « -I» не робити те , що ви думаєте , що робить « -I, --use-compress-program PROG» , ви , ймовірно , хочете «-T, --files-з ФАЙЛА»
Xen2050

Я вважаю, що так | tar czf my_archive.tar.gz -I -має бути| xargs tar Azf my_archive.tar.gz
Олів'є Дулак

@ Xen2050, revзмінює порядок символів у кожному рядку, а не порядок рядків у потоці. Через це sortгрупуйте файли за їх розширенням. Я підозрюю, що це -I -мало бути -T -, що надає список файлів на stdin.
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 ..

Кабачки:

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

Це працює afaik лише на основі файлів. Ви можете бачити, що під час сортування цих трьох тестових файлів у нестиснений архів tar і згодом стискає їх з mksquashfs. З іншого боку, mksqashfs звітуватиме при знаходженні дублікатів у Number of duplicate files foundв stdout.
Іззі

1

У моїй системі lzma test.tarз'являється файл test.tar.lzma 106'3175 байт (1,1 М)


1

Як додаток до "відповіді механічної равлики:

Навіть xz (або lzma) не знайде дублікатів, якщо розмір файлу нестисненого єдиного файлу (або, точніше, відстань між дублікатами) перевищує розмір словника. xz (або lzma), навіть на найвищому рівні, -9eдля цього залишається лише 64 Мб.

На щастя, ви можете вказати свій власний розмір дикторію за допомогою параметра --lzma2=dict=256MB ( --lzma1=dict=256MBдозволено лише при використанні псевдоніма lzma для команди)

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


-2

gzip без комутаторів командного рядка використовує найменший можливий алгоритм для стиснення.

Спробуйте скористатися:

gzip -9 test.tar

Ви повинні отримати кращі результати


1
Насправді різниця мінімальна. Я також спробував bzip2 з подібними результатами.
Гвідо

gzip без комутаторів командного рядка використовує найменший можливий алгоритм для стиснення. => Це неправда - "man gzip" заявляє, що "(t) рівень зажаття за замовчуванням становить -6 (тобто упереджений до високої компресії за рахунок швидкості)." Це справедливо для всіх відомих мені версій gzip, якщо змінна настройка за замовчуванням не змінюється змінною середовища GZIP. Навіть рівень "-9" вам тут не допоможе, як уже пояснено у відповідях.
Gunter Ohrner
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.