Почему файлы tar.xz при использовании библиотеки tar Python получаются в 15 раз меньше, чем tar macOS?

Почему файлы tar.xz при использовании библиотеки tar Python получаются в 15 раз меньше, чем tar macOS?

Контекст

tarЯ сжимаю папки размером ~1,3 ГБ, каждая из которых заполнена 1440 файлами JSON, и обнаруживаю, что разница между использованием этой команды и встроенного в Python метода составляет 15 раз.tarfileбиблиотека на macOS илиРапбиан 10(Бастер)

Минимальный рабочий пример

Этот скрипт сравнивает оба метода:

#!/usr/bin/env python3

from pathlib import Path
from subprocess import call
import tarfile

fullpath = Path("/Users/user/Desktop/temp/tar/2021-03-11")
zsh_out = Path(fullpath.parent, "zsh-archive.tar.xz")
py_out = Path(fullpath.parent, "py-archive.tar.xz")

# tar using terminal
# tar cJf zsh-archive.tar.xz folderpath
call(["tar", "cJf", zsh_out, fullpath])

# tar using tarfile library
with tarfile.open(py_out, "w:xz") as tar:
    tar.add(fullpath, arcname=fullpath.stem)

# Print filesizes
print(f"zsh tar filesize: {round(Path(zsh_out).stat().st_size/(1024*1024), 2)} MB")
print(f"py tar filesize: {round(Path(py_out).stat().st_size/(1024*1024), 2)} MB")

Вывод:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB

Я использую следующие версии:

  • tarна macOS:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
  • tarна Raspbian 10:xz (XZ Utils) 5.2.4 liblzma 5.2.4
  • tarfileБиблиотека Python:0.9.0

Что я пробовал

После сжатия я извлек оба архива и сравнил полученную папку с:

diff -r py-archive-expanded zsh-archive-expanded

Разницы не было.

Если сравнить два tar-архива напрямую, то они покажутся разными:

➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ

Если я проверю архивы с помощью Quicklook (и плагина Betterzip), то увижу, что файлы в архиве упорядочены по-другому:

Слева это zsh-archive.tar.xz, справа это py-archive.tar.xz:

Введите описание изображения здесьвведите описание изображения здесь

Архив zsh использует неизвестный порядок, а архив Python упорядочивает файл по дате изменения. Я не уверен, имеет ли это значение.

Вопрос

Что происходит? Теряю ли я что-то, используя библиотеку Python для сжатия данных? Является ли 15-кратная разница в размере признаком какой-то проблемы? Или я могу спокойно использовать эффективную реализацию Python?

решение1

Короткий ответ: да, использовать Python tarlibдля сжатия данных безопасно, ничего не теряется по сравнению с BSD tar.

Основная проблема: сортировка

Я думаю, что основная проблема в том, что BSD tarи GNU tarбез каких-либо опций сортировки помещают файлы в архив в неопределенном порядке.

У GNU tarесть --sortвозможность:

сортировать записи каталога по ORDER, который является одним из none, name, или inode.
Значение по умолчанию --sort=none, которое сохраняет элементы архива в том же порядке, который возвращается операционной системой.

Тестирование GNUtar

Чтобы проверить это, я установил GNU tarна свой Mac с помощью:

brew install gnu-tar

А затем заархивировал ту же папку, но с --sortопцией:

gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11

Размер архива zsh-archive-sorted.tar.xzсоставляет 1,5 МБ, что соответствует размеру архива, созданного библиотекой Python.

Объединение в отсортированном порядке

Влияние сортировки на размер конечного архива дополнительно демонстрируется путем объединения всех JSON-файлов, отсортированных по имени (в начале которых стоит unixtime создания), а затем их архивирования с помощью BSD tar:

cat *.json > all.txt
tar cJf zsh-cat-archive.tar.xz all.txt

Архив zsh-cat-archive.tar.xzтакже имеет размер 1,5 МБ.

tarfileСортировка Python

Наконец,TarFile.addдокументация функции Pythonподтверждает, что Python tarfileсортирует по умолчанию:

Каталоги добавляются рекурсивно по умолчанию. Этого можно избежать, установив recursive в False. Рекурсия добавляет записи в отсортированном порядке.

Почему сортировка имеет значение

Я думаю, что причина, по которой сортировка оказала такое влияние в моем случае, заключается в следующем:

Мои файлы JSON содержат местоположения сотен транспортных средств. Каждую минуту я считываю все местоположения, но только несколько из этих местоположений имеют разные значения от минуты к минуте.
При сортировке файлов по имени два последующих файла имеют немного отличающиеся символы между собой. По-видимому, это очень благоприятно для эффективности сжатия.

решение2

Попробуйте настроить уровни сжатия в командной строке macOS.

Я знаю, что вы спрашиваете оxzно объяснил вэтот ответ здесьВ старых версиях GZip уровень сжатия можно задать с помощью переменной окружения, например так:

GZIP=-9 tar cf zsh-archive.tar.xz folderpath

Тем не менее, похоже, что это работает только с GZip 1.8 и устарело в более поздних версиях. Поэтому вместо этого используйте опцию -I/ --use-compress-program=COMMANDдля tar; обратите внимание, что эта опция может не работать на macOS, но на всякий случай поместите ее здесь. Таким образом, команда изменится на:

tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath

И да, эти примеры будут сжимать архив Gzip вместо xz, но вы можете легко изменить команду на эту, чтобы использовать ее xzследующим образом:

tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath

Уровень xzсжатия варьируется от -0до -9, значение по умолчанию равно -6; то же самое -9касается и самого высокого уровня сжатия.

Обратите внимание, что xzпо умолчанию он не установлен на macOS. Чтобы установить его на macOS, сначала нужно установитьДомашнее пивоа затем установитьxzчерез Homebrew вот так:

brew install xz

решение3

Заставляет задуматься, что Python использует для сжатия

http://tukaani.org/xz/

Вероятно, он использует вызовы функций в liblzma.Тарвероятно, проходит через команду оболочки xz.

Краткий комментарий по теме --sort=name:

Опция сортировки является относительно недавним усовершенствованием GNU tar и была представлена ​​в версии tar 1.28.

Возможно, он никогда не будет реализован в BSD tar.

Связанный контент