Форматы сжатия: gzip, bzip2, xz, zstd
Сжатие — это compromise между четырьмя метриками: размер результата, время компрессии, время декомпрессии, и memory usage. Не существует «лучшего» алгоритма — каждый занимает свою точку на этом 4D-tradeoff пространстве.
Для DE нужно знать четыре главных формата: gzip, bzip2, xz, zstd. В этом уроке — что выбрать когда.
Сравнение в одной таблице
Trade-offs между размером, скоростью, и совместимостью
Бенчмарк: 1GB CSV-файл
Примерные цифры (зависят от данных):
| Формат | Размер после | Время сжатия | Время разжатия |
|---|---|---|---|
| gzip -6 (default) | 280 MB | 25 sec | 5 sec |
| bzip2 -6 | 220 MB | 90 sec | 25 sec |
| xz -6 | 180 MB | 180 sec | 12 sec |
| zstd -3 (default) | 250 MB | 8 sec | 3 sec |
| zstd -19 (max) | 175 MB | 200 sec | 3 sec |
Главное наблюдение: zstd на дефолтном уровне сжатия — в 3 раза быстрее gzip, и сжимает чуть лучше. На уровне 19 — сжимает как xz, но разжимается в 4 раза быстрее.
Поэтому zstd стал стандартом для нового software: ядро Linux 5.16+ использует zstd для модулей, Docker images используют zstd, btrfs/zfs filesystem compression.
Docker image layers — как zstd связан с OCI image formatgzip — стандарт, доступен везде
# Сжать файл — на месте! Файл заменяется
gzip file.csv
# Результат: file.csv.gz, оригинал удалён
# Сохранить оригинал
gzip -k file.csv
# Результат: file.csv (originally) + file.csv.gz
# Разжать
gunzip file.csv.gz
# Или
gzip -d file.csv.gz
# Прочитать без распаковки
zcat file.csv.gz
gunzip -c file.csv.gz # синоним
# Уровень сжатия 1-9 (1 = fast/large, 9 = slow/small)
gzip -9 file.csv # максимальное сжатие
gzip -1 file.csv # минимальное (быстрое)
# По умолчанию -6
gzip удаляет оригинал по умолчанию! Это часто ловит новичков. Команда gzip file.csv приведёт к тому, что file.csv исчезнет, появится file.csv.gz. Используй -k (keep) чтобы оставить оригинал.
Pipe-friendly
# Сжать на лету в pipeline
cat huge.csv | gzip > compressed.csv.gz
# Разжать на лету (для grep/awk)
zcat file.csv.gz | grep "error"
gunzip -c file.csv.gz | awk -F',' '{print $3}'
# Modern shorthand
zgrep "error" file.csv.gz
Эта pipe-friendly особенность — то, почему gzip везде. Tools умеют работать с gzip напрямую.
bzip2 — больше сжатие, медленнее
bzip2 file.csv
# Результат: file.csv.bz2
bunzip2 file.csv.bz2
# или
bzip2 -d file.csv.bz2
# Pipe
bzcat file.csv.bz2 | head
bzip2 -c huge.csv > huge.csv.bz2
bzip2 теряет популярность. Используется в основном для legacy distribution package. Для DE-задач — практически никогда. Если нужно лучше gzip — берите xz или zstd.
xz — лучшее сжатие из traditional
# Сжать (медленно но компактно)
xz file.csv
# Разжать
unxz file.csv.xz
# или
xz -d file.csv.xz
# Pipe
xzcat file.csv.xz
xz -c huge.csv > huge.csv.xz
# Уровни 0-9 (9 = max compression)
xz -9 file.csv
Где используется:
- Linux kernel distribution (linux-X.Y.Z.tar.xz) — нужно максимальное сжатие при минимальной частоте обновления.
- Большие software releases где компрессия делается один раз, разжатие много.
Для DE — изредка для long-term archival storage.
zstd — современный король
zstd (Zstandard) — разработан Facebook в 2016, открытый исходный код. Идеален когда нужно «и быстро, и хорошо сжатие, и быстро разжимается».
# Установка (Ubuntu 26.04 идёт со zstd по умолчанию)
sudo apt install zstd
# или
brew install zstd
# Сжать
zstd file.csv
# Результат: file.csv.zst (оригинал сохраняется по умолчанию!)
# Удалить оригинал
zstd --rm file.csv
# Разжать
unzstd file.csv.zst
# или
zstd -d file.csv.zst
# Pipe
zstdcat file.csv.zst
zstd -c huge.csv > huge.csv.zst
# Уровни 1-22 (по умолчанию 3)
zstd -1 file.csv # самое быстрое
zstd -19 file.csv # лучше сжатие, медленнее
zstd --long file.csv # large window (для больших файлов с redundancy)
zstd по умолчанию сохраняет оригинал (в отличие от gzip!). Это безопаснее. Чтобы удалить оригинал — --rm.
zstd для streaming
# Postgres dump через pipe в zstd
pg_dump dbname | zstd > backup.sql.zst
# Восстановление
zstd -d backup.sql.zst -c | psql dbname
# или (один шаг)
zstdcat backup.sql.zst | psql dbname
Это главный DE-use case для zstd: streaming compression для backup pipelines. Никакого temporary file, всё в pipe.
tar + сжатие: какое выбрать
# tar + gzip — universally compatible
tar czvf backup.tar.gz dir/
# tar + zstd — современный стандарт DE
tar --zstd -cvf backup.tar.zst dir/
# Извлечь zstd
tar --zstd -xvf backup.tar.zst
# Или (modern tar определит по расширению)
tar xvf backup.tar.zst
Когда выбирать:
Какой компромисс важнее в твоём use case
DE-сценарии
1. Daily backup Postgres
# Маленькая БД (<10GB)
pg_dump dbname | gzip > backup-$(date +%F).sql.gz
# Большая БД, хочется speed
pg_dump dbname | zstd -3 > backup-$(date +%F).sql.zst
# Очень большая, хочется минимум места (archival)
pg_dump dbname | xz > backup-$(date +%F).sql.xz
# Долго, но небольшой результат
2. Сжатие старых логов
# Сжать все .log файлы старше 7 дней
find /var/log/airflow -name '*.log' -mtime +7 -exec gzip {} \;
# С zstd для нового software
find /var/log/airflow -name '*.log' -mtime +7 -exec zstd --rm {} \;
# --rm удалит оригинал после сжатия
# Параллельное сжатие — быстрее на multi-core
find /var/log -name '*.log' -mtime +7 -print0 | \
xargs -0 -P 4 -n 1 gzip
# -P 4 — 4 параллельных процесса
3. CSV -> parquet через сжатие
CSV — большой, parquet — компактный native. Но иногда нужно временно сжать CSV для передачи:
# CSV 1GB -> CSV.zst ~200MB
zstd input.csv -o input.csv.zst
# На той стороне обратно
zstdcat input.csv.zst > input.csv
4. Поиск в сжатых логах
# Без распаковки
zgrep "ERROR" /var/log/airflow/*.log.gz
# Streaming через awk
zcat /var/log/airflow/*.log.gz | awk '/ERROR/ {count++} END {print count}'
# Для zstd
zstdgrep "ERROR" /var/log/airflow/*.log.zst
# или
zstdcat /var/log/airflow/*.log.zst | grep "ERROR"
5. Передача больших данных через сеть
# Старый способ: tar + gzip + ssh
tar czf - data/ | ssh remote "cd /target && tar xzf -"
# Современный: zstd для большей скорости
tar --zstd -cf - data/ | ssh remote "cd /target && tar --zstd -xf -"
# С контролем уровня сжатия (баланс CPU vs network)
tar -cf - data/ | zstd -3 | ssh remote "cd /target && zstdcat | tar xf -"
Когда сжатие не помогает
Уже-сжатые данные не сжимаются (или почти не сжимаются):
- JPEG, PNG, GIF — уже сжаты алгоритмом изображений.
- MP3, MP4, OGG — уже сжаты media-кодеками.
- ZIP, GZ, BZ2, XZ, ZST — уже сжаты.
- Parquet, ORC с compressed columns — уже сжаты.
- Encrypted данные — выглядят случайно, не сжимаются.
Если попытаетесь сжать image.jpg:
ls -l image.jpg image.jpg.gz
# -rw-r--r-- 1 levo levo 2048543 image.jpg
# -rw-r--r-- 1 levo levo 2049012 image.jpg.gz # БОЛЬШЕ!
.gz может быть больше оригинала — gzip добавил metadata. Так что не сжимайте сжатое.
Compression ratios — что зависит от данных
Алгоритмы хорошо сжимают данные с redundancy:
- Text (CSV, JSON, XML, plaintext): 70-90% reduction.
- Logs: 80-95% reduction (много повторяющихся pattern).
- HTML: 70-80% reduction.
- Source code: 60-80% reduction.
- Random binary: 0-5% reduction.
- Already compressed: ≈ 0% (или хуже).
DE-наблюдение: ETL processed CSV logs могут сжиматься в 20 раз через zstd с высоким уровнем — потому что много повторяющихся timestamps, IP, user IDs.
Парсинг сжатого файла без полной распаковки
Для огромных файлов — не делать полный gunzip:
# Прочитать первые 100 строк
zcat huge.csv.gz | head -100
# Посчитать строки
zcat huge.csv.gz | wc -l
# AWK обработка
zcat huge.csv.gz | awk -F',' '$3 > 1000 {print}'
# Сжатая sortable стрим
zcat *.csv.gz | sort | uniq -c | sort -rn | head
Поток через zcat не загружает файл целиком в память — он стримит. Можно обрабатывать гигабайты без проблем.
Попробуй сам
# 1. Создай большой текстовый файл
seq 1 1000000 | awk 'BEGIN{srand()} {print $1","rand()*1000","strftime("%Y-%m-%d")}' > /tmp/big.csv
ls -lh /tmp/big.csv
# 2. Сжатие разными форматами
gzip -k -6 /tmp/big.csv # default level 6
bzip2 -k /tmp/big.csv
xz -k /tmp/big.csv
zstd -k /tmp/big.csv
ls -lh /tmp/big.csv*
# 3. Сравни время
time gzip -k -9 /tmp/big.csv -c > /tmp/big.gz9
time zstd -19 /tmp/big.csv -o /tmp/big.zst19
time xz -k -9 /tmp/big.csv -c > /tmp/big.xz9
ls -lh /tmp/big.{gz9,zst19,xz9}
# 4. Pipe-обработка без распаковки
zcat /tmp/big.csv.gz | head -5
zcat /tmp/big.csv.gz | wc -l
zcat /tmp/big.csv.gz | awk -F',' '$2 > 900 {count++} END {print count}'
# 5. tar + сжатие
mkdir /tmp/dir-test
echo "data" > /tmp/dir-test/file1
echo "data2" > /tmp/dir-test/file2
tar czvf /tmp/test.tar.gz /tmp/dir-test/
tar --zstd -cvf /tmp/test.tar.zst /tmp/dir-test/
ls -lh /tmp/test.tar*
# 6. Cleanup
rm /tmp/big* /tmp/test*
rm -rf /tmp/dir-test
Cross-link: предыдущий урок 01 — tar операции. Следующий урок 03 — zip для совместимости с Windows. Урок 04 — workflows для DE.