Архивация данных на сервере это древняя задача, а стандартный рефлекс tar czf берётся из мышечной памяти девяностых. Но у gzip есть современный конкурент: zstd от согражданина индустрии сжатия с потерями времени, а не данных. На типовых серверных данных zstd на стандартном уровне жмёт сопоставимо с gzip, а работает в 3-5 раз быстрее, а в режиме многопоточности разрыв доходит до десятикратного. Статья разбирает реальные числа, команды и сценарии, где какой вариант выбирать.
Почему gzip медленный и что сделал zstd
gzip работает по алгоритму DEFLATE 1992 года: один поток, скромная степень сжатия, скорость порядка 30-60 МБ/с на ядро. На современном сервере с 16 ядрами и NVMe на 3 ГБ/с чтения это означает, что сжатие становится бутылочным горлом ещё до того, как закончился диск: архивация ста гигабайт занимает час, при этом 15 ядер скучают. Параллельный pigz решает половину задачи - он раскидывает DEFLATE по ядрам - и действительно ускоряет процесс в разы, но алгоритм остаётся старым, и предел его примерно на уровне нескольких сотен мегабайт в секунду.
zstd это другая архитектура: алгоритм 2015 года, оптимизированный под современные процессоры; на уровне по умолчанию (3) сжимает сопоставимо с gzip -6, но со скоростью в сотни мегабайт в секунду на один поток; докручивается длинными окнами под большие данные (-3 в дефолте превращается в -19 для максимального сжатия); понимает словари для мелких файлов и самое главное - многопоточность встроена в CLI через -T, без сторонних утилит. zstdmt, как его вызывают, на 8 ядрах обрабатывает данные со скоростью, при которой уже NVMe становится узким местом.
Команды вместо теории, как пишется рабочая строка
Классика жанра выглядит так: tar czf backup.tar.gz /var/lib/myapp/. Современная замена: tar -I zstd -cf backup.tar.zst /var/lib/myapp/ или явно тар из-под zstd с потоком: tar -cf - /var/lib/myapp | zstd -3 -T0 > backup.tar.zst. Флаг -I указывает tar пользоваться внешней программой сжатия; zstd -T0 задействует все ядра; уровень 3 это разумный дефолт, уровень 6 накачивает сжатие ценой времени, уровень 19 уже конкурирует с xz по плотности, но работает медленно.
Чтение выполняется симметрично: tar -I zstd -xf backup.tar.zst или с автоопределением tar xf backup.tar.zst, потому что современный GNU tar сам распознаёт формат архива по служебным байтам сигнатуры, и флаг -I при распаковке можно опустить. Для оценки объёма до записи архива работает быстрая оценка: du -sh каталог даст понимание сырых данных, а zstd --train не требуется, если вы не жмёте миллионы мелких файлов одного типа.
Совместимость с внешним миром это самое слабое место zstd: формат tar.gz читает всё что угодно начиная с Windows, у старых серверов zstd может не быть установлен (доставляется пакетом zstd в два счёта, но на чужом железе требует права). Для обмена архивами с внешними контрагентами gz остаётся lingua franca; для внутренних бэкапов, где распаковка происходит на своих машинах, zstd выигрывает всухую.
Цифры на реальных данных, что даёт переход
Возьмём три типовых набора данных и набор скоростей на сервере класса 8 vCPU с NVMe. Набор первый - дерево исходников и git-репозиториев, 10 ГБ, сильно сжимаемое: gzip пишет архив примерно за 4 минуты, pigz -8 за 40 секунд, zstd -3 -T0 за 15-20 секунд, а zstd -19 выдаёт чуть меньший файл за минуту с хвостиком. Набор второй - база PostgreSQL в формате dump, 50 ГБ уже отчасти сжатого текста: gzip уходит на 15-20 минут, zstd -3 справляется за полторы-две минуты, разница в размере не превышает 10-15% в пользу gzip. Набор третий - медиафайлы и бинарники, слабо сжимаемые: всё сольётся в копирование со свистом, zstd тут проявляет себя тем, что вообще не тратит впустую время: его адаптивные эвристики быстро понимают, что жать нечего.
На практике это меняет расписание бэкапов: ночной dump 200 ГБ, который с gzip не влезал в окно обслуживания, с zstd -3 спокойно выполняется за 6-8 минут, и резервная копия успевает сделаться не только до начала рабочего дня, но и до первой чашки кофе самого раннего разработчика. Дополнительный эффект - CPU остаётся свободным для приложений, потому что zstd на низком уровне нагружает всего пару ядер.
Хранение тоже выигрывает от быстрой распаковки: zstd разжимает данные со скоростью, близкой к скорости чтения диска (сотни мегабайт и больше в секунду на ядро), поэтому restore делается за время копирования, а не за часы расшифровки сжатия. Для процедур аварийного восстановления, где важен каждый час, zstd один умеет быть и компактным, и быстрым одновременно - xz и bzip2 быстрым не бывают никогда.
Когда оставлять gzip и где место xz
Честный ответ на вопрос: gzip остаётся стандартом обмена. Пакеты дистрибутивов, архивы для общей отдачи, артефакты для систем, о которых вы ничего не знаете - всё это tar.gz. xz и его потомок lzma выбирают там, где важен каждый процент размера и неважна скорость: раздача исходников, холодные дампы 'распакуют один раз'. zstd занимает золотую середину: ежедневные бэкапы, временные архивы, переносы данных между серверами, контейнерные образы (docker давно понимает zstd-слои), всё, где итераций много и время дорого.
В заключение опишем дисциплину, которой придерживаются практики: внутри своей инфраструктуры по умолчанию tar -I zstd -cf для всего нового; уровень -3 для бэкапов и -6, если архив маленький, а хранить его предстоит долго; наружу, не зная экосистемы получателя - gzip. Такая простая развилка превращает выбор алгоритма из ежемесячного холивара в одну строчку в поваренной книге команды.
## Интеграция в скрипты бэкапа, полный рабочий пример
Живой скрипт бэкапа базы данных с zstd выглядит компактно, а работает предсказуемо:
#!/bin/bash
set -euo pipefail
DEST=/srv/backups/$(date +%F)
mkdir -p "$DEST"
pg_dumpall | zstd -6 -T0 -o "$DEST/db.sql.zst"
tar -I 'zstd -3 -T0' -cf "$DEST/etc.tar.zst" /etc
sha256sum "$DEST"/*.zst > "$DEST/SHA256SUMS"
find /srv/backups -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
Три безмолвных детали здесь критичны. Первая: set -euo pipefail валит скрипт при любой ошибке середины - иначе можно неделями копировать недожатый поток, радуясь малому размеру файла. Вторая: контрольные суммы пишутся рядом, и restore начинается с их проверки. Третья: ротация старых каталогов защищает диск от молчаливого переполнения, что классически обнаруживается в самый неподходящий момент ночной эксплуатации.
Вторая интеграционная точка это передача архивов по сети. Потоковая связка tar -I zstd -cf - /data | ssh backup@host 'cat > /srv/incoming/data.tar.zst' выполняет архивацию и пересылку одновременно, без промежуточного файла на исходном сервере. На толстом канале такая схема ограничивается только скоростью чтения с диска, потому что zstd успевает жать быстрее сети. Скорость критична при миграциях: 500 ГБ данных между двумя ДЦ это вопрос пары часов, а не ночи с риском оборваться на середине.
Словари и длинные окна, выжимаем максимум из мелких файлов
Отдельная суперсила zstd - словари. Когда жмутся тысячи однотипных мелких документов (логи JSON, записи очередей, экспорт пользователей), энтропия каждого отдельного маленькая, но общие паттерны повторяются. Словарь обучается на выборке: zstd --train каталог-образца/* -o dict.zstd, и дальше каждый файл жмётся с -D dict.zstd, давая на мелочи коэффициенты сжатия, недостижимые ни gzip, ни самим zstd без словаря. Внутренние бэкапы событий из Kafka это классический пример, где словарь сокращает объём в несколько раз по сравнению с обычным сжатием.
Второй регулятор это длинное окно: --long=27 включает окно 128 МБ вместо стандартных 8 и вылавливает повторы на больших сырых данных - образах дисков, дампах, видеоархивах. Распаковщик тогда требует столько же памяти на окно, поэтому для совместимости с незнакомыми получателями --long отключают, а внутри своей инфраструктуры это чистая выгода.
Честный замер своих данных, методика на один вечер
Прежде чем переписывать все бэкапные скрипты, замерьте свои реальные данные. Берётся типовой дамп или каталог, и прогоняются четыре кандидата: gzip -6, pigz -6 -p8, zstd -3 -T0 и zstd -6 -T0. Фиксируются три числа: время сжатия, размер архива и время распаковки (zstd -d или соответствующая команда). На большинстве серверных нагрузок таблица выглядит однотипно: zstd -3 быстрее gzip в 4-7 раз при размере больше на 5-15%, zstd -6 жмёт примерно как gzip -9, но втрое быстрее; распаковка zstd стабильно в 4-6 раз быстрее gunzip.
Этих цифр достаточно, чтобы принять решение для своего профиля хранения данных. И они же послужат аргументом в разговоре с коллегами: когда на графиках прогона видно, что ночное окно бэкапа сужается с трёх часов до двадцати минут, возражать против нового формата становится трудно.
Подводные камни, на которых спотыкаются чаще всего
Первая засада это расширение и служебные байты сигнатуры: zstd-архив начинается с последовательности 28 B5 2F FD, и tar прекрасно читает такой файл без подсказки, а вот старые сборки tar выпуска более чем десятилетней давности могут быть бессильны. Вторая засада - порядок флагов: zstd -o заставляет записывать именно в указанный файл и отлично работает, но требует аккуратности на равенство аргументов; zstd файл > архив более универсальна в древних скриптах. Третья - память: высокие уровни (-19 и выше, опции ultra 20-22) держат больше гигабайта оперативки на поток, и на слабых виртуалках -19 вызывает OOM, тогда как -6 -T0 уживается везде.
Четвёртая мелочь - переносимость: zstd -д - а нет ли на целевой машине. Стандартная привычка проверить наличие утилиты через command -v zstd до цепочек пайпов избавляет от обидных сюрпризов при восстановлении на голой машине с live-носителя, который собран десять лет назад. Кстати, современные SystemRescue и подобные спасательные образы уже давно включают zstd из коробки, что лишний раз подтверждает статус формата.
Резюме для шпаргалки на стене
Пишем внутри контура: tar -I 'zstd -3 -T0' -cf данные.tar.zst. Читаем: tar xf данные.tar.zst. Оцениваем размер до: du -sh. Проверяем после: zstd -t архив тестирует целостность без распаковки. Отправляем наружу незнакомому получателю: tar czf как обычно. Ставим длинные окна только своим. Учим словарь для мелочей. И помним про sha256sum рядом с архивом, потому что целостность проверяется до начала restore, а не после того, как половина данных разъехалась по серверу. С такой шпаргалкой переход на zstd перестаёт быть проектом и становится привычкой, которая возвращает вам часы каждую неделю - время, раньше потраченное на томительное ожидание сжатия gzip.
Минута разговора о скорости восстановления
Отдельного абзаца заслуживает то, как zstd меняет восстановление после аварии. Сравните два сценария: restore из gzip-архива на 200 ГБ займёт десятки минут только на decompress, затем поток ещё запишется на диск; restore из zstd часто оказывается быстрее, чем прямое копирование несжатых данных по сети, потому что по проводу бежит сжатый поток, а распаковка на приёмной стороне дешевле медленного чтения чужого диска. Проектируйте процедуру DR с этим в голове: быстрая распаковка не просто приятна, она уменьшает окно простоя, а значит напрямую влияет на SLA, доступность и ваш спокойный сон.
Ещё одна практическая мысль: zstd поддерживает список уровней такой ширины, что один и тот же инструмент закрывает и 'быстрое временное сжатие для пересылки' (-1 -T0), и 'холодное хранение' (-19 --long). Это редкое свойство, при котором в скриптах достаточно помнить один синтаксис, а политика выражается единственным числом уровня - и это заметно упрощает поддержку инфраструктуры в долгую.
Последний аргумент за переход: дисковая экономия на самих бэкапах быстро окупает потраченный на миграцию формат вечер - репозитории с архивами растут предсказуемо, и десять процентов выигрыша на каждом снапшоте за год превращаются в весьма осязаемые сотни гигабайт.