Crea y compara archivos tar con Zstd y LZ4
Envía la salida de tar a zstd o lz4
mediante una tubería para elegir explícitamente el compresor y sus ajustes.
La siguiente guía crea ambos archivos tar, restaura su contenido y mide el tiempo de compresión
y el tamaño con la misma entrada. Cada archivo tar recibe un nombre nuevo solo después de que
la compresión finaliza correctamente; volver a ejecutar un comando no puede reemplazar uno existente.
Comprueba tus herramientas
Usa Linux con Bash, GNU tar, GNU coreutils, diff, Python 3.9 o posterior,
Zstd y LZ4 1.10.0 o posterior. Los siguientes comandos se probaron con GNU tar 1.35, Bash 5.3.15,
Zstd 1.5.7, LZ4 1.10.0 y Python 3.14.7. Instala las herramientas que falten mediante el gestor de
paquetes de tu distribución y luego comprueba lo siguiente:
tar --version
bash --version
zstd --version
lz4 --version
python3 --version
ln --version
La implementación de tar importa. GNU tar admite --zstd,
pero no tiene la opción --lz4.
bsdtar admite ambas opciones al crear archivos tar
y detecta esos formatos al leerlos. Esta guía usa GNU tar y compresores independientes;
no es una guía de shell para macOS/BSD, en particular porque ln -T,
que se usa más adelante, es una opción de GNU.
Crea un directorio de origen reproducible
Ejecuta cada bloque de Bash desde el mismo directorio inicial. Los paréntesis mantienen
cd y las opciones de shell dentro del ámbito de cada bloque.
La preparación rechaza reutilizar un directorio tar-lab existente,
por lo que no puede sobrescribir un experimento anterior.
(
set -euo pipefail
mkdir tar-lab
cd -- tar-lab
mkdir project
python3 - <<'PY'
from pathlib import Path
import random
project = Path("project")
with (project / "records.csv").open("xb") as output:
for index in range(1_000_000):
output.write(f"{index:08d},user-{index % 1000:04d},GET,/assets/app.js,200\n".encode())
(project / "binary.bin").write_bytes(random.Random(0).randbytes(8 * 1024 * 1024))
(project / "empty.txt").touch()
(project / "omit.log").write_text("Exclude this log.\n", encoding="utf-8")
PY
)
Este conjunto de prueba combina texto estructurado con bytes seudoaleatorios deterministas y un archivo vacío. Mantén el origen sin cambios mientras lo empaquetas. Que un comando tar finalice correctamente no implica que se obtenga una instantánea coherente de archivos que una aplicación está modificando activamente.
Crea archivos tar sin reemplazar resultados anteriores
Ambos ejemplos empaquetan project/ y omiten los nombres que coinciden con
*.log, incluidos los registros en subdirectorios. El patrón entre comillas
llega a tar sin que el shell lo expanda. La opción -c de tar crea el
archivo tar; -f - lo envía a la salida estándar. El compresor escribe en
un directorio temporal nuevo junto al archivo tar final, fuera del árbol de origen.
pipefail hace que un fallo del productor tar provoque el fallo del pipeline,
aunque el compresor finalice correctamente.
No protege un archivo abierto mediante una redirección del shell: >
puede truncar una copia de seguridad anterior antes de que se ejecute tar. Aquí solo se redirige
la salida temporal. Una vez superada la prueba de integridad del compresor,
ln -T de GNU asigna el nombre final
a esos bytes ya completos. Sin -f, rechaza cualquier archivo,
directorio o enlace simbólico existente.
Los nombres temporal y final comparten un mismo sistema de archivos, como requieren los enlaces
duros. El manejador de salida elimina el directorio temporal tanto al finalizar normalmente como
al producirse un fallo.
Crea el archivo tar con Zstd
Usa el nivel tres y un trabajador de compresión. En el
manual de la CLI de Zstd, -T1
significa un trabajador de compresión además de la E/S; es diferente de
--single-thread.
(
set -euo pipefail
cd -- tar-lab
stage=$(mktemp -d .zstd.XXXXXX)
trap 'rm -rf -- "$stage"' EXIT
tar --exclude='*.log' -cf - project | zstd -3 -T1 -c > "$stage/archive"
zstd -t "$stage/archive"
ln -T -- "$stage/archive" project.tar.zst
printf 'Created project.tar.zst\n'
)
Crea el archivo tar con LZ4
Usa también aquí el nivel uno y un trabajador de compresión. LZ4 1.10.0 añadió compresión multihilo;
su manual de la CLI documenta -T1.
Las versiones anteriores de LZ4 no admiten esa opción.
(
set -euo pipefail
cd -- tar-lab
stage=$(mktemp -d .lz4.XXXXXX)
trap 'rm -rf -- "$stage"' EXIT
tar --exclude='*.log' -cf - project | lz4 -1 -T1 -c > "$stage/archive"
lz4 -t "$stage/archive"
ln -T -- "$stage/archive" project.tar.lz4
printf 'Created project.tar.lz4\n'
)
Una ejecución correcta termina con el mensaje Created correspondiente.
Una nueva ejecución devuelve un estado distinto de cero y deja intactos los bytes del archivo tar
existente, aunque project/ haya desaparecido. Para crear otro archivo tar,
elige otro nombre de archivo final; no elimines una copia de seguridad anterior solo para que
una nueva ejecución finalice correctamente.
Una terminación forzada o un corte de energía pueden dejar archivos temporales. Estos comandos
no garantizan la persistencia de los datos ante una caída del sistema.
Restaura y compara los archivos
Una prueba de integridad comprueba el flujo comprimido, pero no demuestra que contenga los archivos que querías guardar. Restaura el contenido en un directorio nuevo y compáralo con el origen sin cambios:
(
set -euo pipefail
cd -- tar-lab
zstd -t project.tar.zst
mkdir restored-zstd
zstd -dc project.tar.zst | tar -xf - -C restored-zstd
diff -r --exclude='*.log' project restored-zstd/project
printf 'Zstd restore matches the source.\n'
)
(
set -euo pipefail
cd -- tar-lab
lz4 -t project.tar.lz4
mkdir restored-lz4
lz4 -dc project.tar.lz4 | tar -xf - -C restored-lz4
diff -r --exclude='*.log' project restored-lz4/project
printf 'LZ4 restore matches the source.\n'
)
No debería aparecer ninguna salida de diff, y sí el mensaje de éxito
correspondiente. Los árboles restaurados contienen records.csv,
binary.bin y empty.txt; omit.log
no está presente. Esta comparación abarca los nombres y el contenido de los archivos, no los
propietarios, los permisos ni todos los metadatos del sistema de archivos. Una nueva ejecución de
la restauración rechaza el directorio de restauración existente.
Un fallo de extracción puede dejar un árbol nuevo parcial, así que considera que la restauración
fue correcta solo después de que la comparación se complete sin diferencias. Usa estos comandos
con archivos tar que hayas creado y en los que confíes.
Mide las ventajas y limitaciones con el mismo flujo tar
Ejecuta este bloque desde el directorio inicial después de la preparación. Crea un archivo tar temporal sin comprimir con un orden de elementos, marcas de tiempo y propietarios fijos, y luego mide cada compresor y decodificador. Se realiza una ejecución de calentamiento y cinco ejecuciones medidas por códec. Cada archivo tar decodificado debe coincidir con los bytes de entrada. Solo se reutilizan los archivos temporales privados de la prueba de rendimiento; tus archivos tar y las restauraciones no se modifican.
python3 - <<'PY'
import hashlib
from pathlib import Path
from statistics import median
import subprocess
from tempfile import TemporaryDirectory
from time import perf_counter
def digest(path):
return hashlib.sha256(path.read_bytes()).digest()
def timed(command, source, destination):
with source.open("rb") as stdin, destination.open("wb") as stdout:
started = perf_counter()
subprocess.run(command, stdin=stdin, stdout=stdout, check=True)
return perf_counter() - started
with TemporaryDirectory(prefix="benchmark-", dir="tar-lab") as temporary:
work = Path(temporary)
raw = work / "input.tar"
with raw.open("xb") as output:
subprocess.run([
"tar", "--sort=name", "--mtime=@0", "--owner=0", "--group=0",
"--numeric-owner", "--exclude=*.log", "-cf", "-", "project",
], cwd="tar-lab", stdout=output, check=True)
expected = digest(raw)
raw_size = raw.stat().st_size
print(f"tar bytes: {raw_size}")
print("codec bytes compressed/tar compress_s decompress_s")
for codec, options in [("zstd", ["-3", "-T1"]), ("lz4", ["-1", "-T1"])]:
packed, decoded = work / codec, work / "decoded.tar"
samples = []
for run in range(6):
compress = timed([codec, *options, "-q", "-c"], raw, packed)
decompress = timed([codec, "-q", "-dc"], packed, decoded)
if digest(decoded) != expected:
raise RuntimeError(f"{codec} changed the tar bytes")
if run > 0:
samples.append((compress, decompress))
size = packed.stat().st_size
print(f"{codec} {size} {size / raw_size:.3f} "
f"{median(c for c, d in samples):.4f} {median(d for c, d in samples):.4f}")
PY
Los tiempos incluyen el inicio de subprocesos y la E/S de archivos locales, pero excluyen la creación del archivo tar, la extracción de sus elementos y la comparación SHA-256. Son mediciones con la caché ya cargada, no una prueba de rendimiento del disco. La proporción se calcula dividiendo los bytes comprimidos entre los bytes del archivo tar sin comprimir; cuanto menor sea, mejor.
El 24 de septiembre de 2026, el conjunto de prueba produjo un archivo tar de 50.401.280 bytes en Linux x86-64 con una CPU AMD Ryzen AI Max+ 395. Con las versiones de las herramientas indicadas anteriormente, las medianas de los tiempos transcurridos fueron:
| Ajustes del compresor | Bytes del archivo tar | Comprimido/tar | Compresión | Descompresión |
|---|---|---|---|---|
Zstd -3 -T1 | 8.946.393 | 0,178 | 0,0281 s | 0,0124 s |
LZ4 -1 -T1 | 15.830.951 | 0,314 | 0,0519 s | 0,0332 s |
Zstd produjo el archivo tar más pequeño y terminó ambas operaciones antes en esta ejecución. La estructura CSV repetida y los tiempos de una sola máquina no establecen una clasificación universal. Sustituye el conjunto de prueba por archivos representativos antes de elegir un compresor para tu carga de trabajo y comprueba que el destinatario pueda decodificar el formato. Las ejecuciones breves son sensibles a otras actividades de la máquina.
Usa el resultado en un flujo de trabajo de copias de seguridad
Mantén la compresión separada de la selección de archivos y del historial de copias de seguridad.
Seleccionar archivos modificados recientemente con find no registra
las eliminaciones. Para obtener una cadena de cambios que puedas restaurar, sigue la
guía de copias de seguridad incrementales de GNU tar.
Programa una copia de seguridad solo después de probar sus procedimientos ante fallos y de
restauración, y conserva los archivos tar de copias anteriores creadas correctamente hasta que
tu política de retención permita eliminarlos.
Para las transferencias por SSH, copia un archivo tar completo y verifícalo en el destino. Si divides un archivo tar en partes, conserva su orden exacto para volver a unirlas y verifica el archivo reconstruido antes de restaurarlo. Ni el transporte ni la división cambian la elección del compresor evaluada aquí.
