Contexto
Estoy comprimiendo carpetas de ~1,3 GB, cada una llena con 1440 archivos JSON y descubro que hay una diferencia de 15 veces entre usar el tar
comando y el integrado de Python.tarfile
biblioteca en macOS oraspbian 10(Destructor)
Ejemplo de trabajo mínimo
Este script compara ambos métodos:
#!/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")
La salida es:
zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB
Las versiones que uso son las siguientes:
tar
en MacOS:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
tar
en Raspbian 10:xz (XZ Utils) 5.2.4 liblzma 5.2.4
tarfile
Biblioteca de Python:0.9.0
Cosas que he probado
Después de la compresión, extraje ambos archivos y comparé la carpeta resultante con:
diff -r py-archive-expanded zsh-archive-expanded
No hubo diferencia.
Si comparo los dos archivos tar directamente, parecen diferentes:
➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ
Si inspecciono los archivos con Quicklook (y el complemento Betterzip), veo que los archivos en el archivo están ordenados de una manera diferente:
La izquierda es zsh-archive.tar.xz
, la derecha es py-archive.tar.xz
:
El archivo zsh utiliza un orden desconocido y el archivo Python ordena el archivo por fecha de modificación. No estoy seguro si eso importa.
Pregunta
¿Qué está pasando? ¿Estoy perdiendo algo al usar la biblioteca Python para comprimir mis datos? ¿Es la diferencia de tamaño de 15 veces un indicador de algún problema? ¿O puedo seguir adelante y utilizar con seguridad la implementación eficiente de Python?
Respuesta1
Respuesta corta: sí, es seguro usar Python tarlib
para comprimir los datos, no se pierde nada en comparación con BSD tar
.
Problema de fondo: clasificación
Creo que el problema subyacente es que BSD tar
y GNU, tar
sin ninguna opción de clasificación, colocan los archivos en el archivo en un orden indefinido.
GNU tar
tiene una --sort
opción:
ordenar las entradas del directorio según
ORDER
, cuál es uno denone
,name
, oinode
.
El valor predeterminado es--sort=none
, que almacena los miembros del archivo en el mismo orden en que los devuelve el sistema operativo.
Probando GNUtar
Para probar esto instalé GNU tar
en mi Mac con:
brew install gnu-tar
Y luego tarreó la misma carpeta, pero con la --sort
opción:
gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11
El zsh-archive-sorted.tar.xz
archivo es de 1,5 MB, igual al tamaño del archivo creado por la biblioteca Python.
Concatenar en orden ordenado
El efecto que tiene la clasificación en el tamaño del archivo final se demuestra concatenando primero todos los archivos JSON ordenados por nombre (que tiene la creación de Unixtime al principio) y luego tar con BSD tar
:
cat *.json > all.txt
tar cJf zsh-cat-archive.tar.xz all.txt
El zsh-cat-archive.tar.xz
archivo también pesa 1,5 MB.
tarfile
Clasificación de Python
Finalmente, eldocumentación de la TarFile.add
función de Pythonconfirma que Python tarfile
ordena de forma predeterminada:
Los directorios se agregan de forma recursiva de forma predeterminada. Esto se puede evitar estableciendo recursivo en False. La recursión agrega entradas en orden.
Por qué es importante ordenar
Creo que la razón por la que la clasificación tiene tal impacto en mi caso es la siguiente:
Mis archivos JSON contienen ubicaciones de cientos de vehículos. Cada minuto leo todas las ubicaciones, pero sólo algunas de estas ubicaciones tienen un valor diferente de un minuto a otro.
Al ordenar los archivos por nombre, dos archivos posteriores tienen caracteres ligeramente diferentes entre ellos. Aparentemente esto es muy favorable para la eficiencia de la compresión.
Respuesta2
Intente configurar los niveles de compresión en la línea de comando de macOS.
Sé que estás preguntando sobrexz
pero explicado enesta respuesta aquí, en versiones anteriores de GZip puedes establecer el nivel de compresión con una variable de entorno como esta:
GZIP=-9 tar cf zsh-archive.tar.xz folderpath
Dicho esto, parece que sólo funciona con GZip 1.8 y se deprecia en versiones posteriores. Entonces use la opción -I
/ --use-compress-program=COMMAND
para tar en su lugar; Tenga en cuenta que es posible que esta opción no funcione en macOS, pero colóquela aquí de todos modos por si acaso. Entonces el comando cambiaría a:
tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath
Y sí, estos ejemplos comprimirían el archivo Gzip en lugar de xz
, pero puedes cambiar fácilmente el comando a este para usarlo xz
así:
tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath
El xz
nivel de compresión varía de -0
a -9
y el valor predeterminado es -6
; también lo -9
es el nivel de compresión más alto.
Solo tenga en cuenta que xz
no está instalado en macOS de forma predeterminada. Para instalarlo en macOS primero debes instalarcerveza caseray luego instalarxz
vía Homebrew así:
brew install xz
Respuesta3
Me pregunto qué usa Python para la compresión.
Probablemente esté usando las llamadas a funciones en liblzma.Alquitránprobablemente esté canalizando el comando xz shell.
Un comentario rápido sobre --sort=name
:
La opción de clasificación es una mejora relativamente reciente de GNU tar y se introdujo en la versión tar 1.28.
Es posible que nunca se implemente en BSD tar.