PostgreSQLБэкапыDevOps

Бэкап PostgreSQL: pg_dump, автоматизация и восстановление

7 мин чтения

База данных — обычно самое дорогое, что есть в проекте. Код лежит в Git и восстанавливается за минуту, а потерянные данные пользователей не возвращаются ниоткуда. Резервная копия существует ровно до первого инцидента — а проверенная резервная копия выручает уже во время него.

Ниже — практическое руководство по бэкапам PostgreSQL: какие бывают, как их автоматизировать, куда складывать и как убедиться, что из них действительно можно восстановиться.

Логический и физический бэкап

У PostgreSQL два принципиально разных подхода к резервным копиям.

Логический бэкап (pg_dump, pg_dumpall) — это, по сути, набор SQL-команд или сжатый архив, который воссоздаёт схему и данные. Он переносим между версиями и архитектурами, удобен для одной базы и читаем. Минус — медленнее на больших объёмах и не хранит точное физическое состояние.

Физический бэкап (pg_basebackup, копия каталога данных) — это побайтовая копия файлов кластера. Быстрее на больших базах и нужен для Point-in-Time Recovery, но привязан к версии и платформе.

Для большинства проектов до десятков гигабайт логического бэкапа через pg_dump достаточно. С него и начнём.

Форматы pg_dump

У pg_dump есть несколько форматов вывода, и выбор формата важнее, чем кажется.

  • plain (-Fp) — обычный SQL-файл. Читаемый, но восстанавливается только целиком и без параллелизма.
  • custom (-Fc) — сжатый бинарный формат. Позволяет выборочное восстановление таблиц и параллельную загрузку. Рекомендуемый вариант по умолчанию.
  • directory (-Fd) — каталог файлов, поддерживает параллельный дамп (-j), что заметно ускоряет выгрузку больших баз.

Базовая команда для одной базы в custom-формате:

pg_dump -Fc -h localhost -U appuser -d appdb -f appdb.dump

Для большой базы — directory-формат с параллелизмом по числу ядер:

pg_dump -Fd -j 4 -h localhost -U appuser -d appdb -f appdb_backup/

Роли, права и список баз pg_dump не выгружает. Чтобы сохранить глобальные объекты кластера (пользователей, табличные пространства), используется pg_dumpall --globals-only:

pg_dumpall -h localhost -U postgres --globals-only -f globals.sql

Восстановление

Custom- и directory-форматы восстанавливаются через pg_restore, plain-дамп — через psql.

# создать чистую базу
createdb -h localhost -U postgres appdb_restored

# восстановить из custom-дампа в 4 потока
pg_restore -h localhost -U postgres -d appdb_restored -j 4 appdb.dump

Флаг --clean добавит команды удаления объектов перед восстановлением — удобно при накатывании дампа поверх существующей базы, но опасно, если перепутать целевую базу. Восстанавливать всегда стоит в отдельную базу, а не поверх боевой.

Автоматизация через cron

Ручной бэкап забывают сделать ровно в тот день, когда он нужен. Минимальный скрипт, который снимает дамп, сжимает его и удаляет копии старше N дней:

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR=/var/backups/postgres
KEEP_DAYS=14
STAMP=$(date +%Y-%m-%d_%H-%M)

mkdir -p "$BACKUP_DIR"
pg_dump -Fc -h localhost -U appuser -d appdb \
  -f "$BACKUP_DIR/appdb_$STAMP.dump"

# удалить копии старше KEEP_DAYS
find "$BACKUP_DIR" -name 'appdb_*.dump' -mtime +"$KEEP_DAYS" -delete

Пароль не стоит передавать в команде — для неинтерактивного запуска используется файл ~/.pgpass с правами 600:

localhost:5432:appdb:appuser:secret

Запуск каждую ночь в 3:30 через crontab -e:

30 3 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1

Флаг set -euo pipefail важен: без него скрипт «успешно» завершится даже после ошибки pg_dump, и в логе останется ноль предупреждений, а в каталоге — битый файл.

Правило 3-2-1: бэкап на том же сервере — не бэкап

Если дамп лежит на том же диске, что и база, то отказ диска или потеря сервера уносит и базу, и копию. Классическое правило надёжного хранения — 3-2-1:

  • 3 копии данных,
  • на 2 разных носителях,
  • 1 из них — за пределами площадки (offsite).

На практике для небольшого проекта это означает: рабочая база, локальный дамп и выгрузка в объектное хранилище. Загрузить готовый дамп в S3-совместимое хранилище можно одной командой:

# через aws-cli (подходит и для Yandex Object Storage, Backblaze B2, MinIO)
aws s3 cp "$BACKUP_DIR/appdb_$STAMP.dump" \
  s3://my-backups/postgres/ --endpoint-url https://storage.yandexcloud.net

Шифровать дамп перед отправкой стоит, если в нём есть персональные данные — например, через gpg или restic, который умеет и инкрементальные шифрованные бэкапы, и ротацию.

Самое важное: проверка восстановления

Непроверенный бэкап — это бэкап в состоянии кота Шрёдингера: неизвестно, рабочий он или нет, пока не понадобится. Типичные сюрпризы вскрываются именно при восстановлении: дамп снимался без нужной роли, кончилось место, версия pg_restore несовместима, в копию не попала схема.

Минимальная регулярная проверка — развернуть свежий дамп в одноразовую базу и убедиться, что данные на месте:

createdb appdb_test
pg_restore -d appdb_test -j 4 appdb.dump
psql -d appdb_test -c "SELECT count(*) FROM users;"
dropdb appdb_test

Это стоит делать по расписанию, а не один раз после настройки. Бэкап, который ни разу не разворачивали, — это гипотеза, а не страховка.

Point-in-Time Recovery — кратко

Логический дамп возвращает состояние на момент снятия. Если нужно восстановиться на произвольную секунду (например, «за минуту до ошибочного DELETE»), требуется PITR: базовая физическая копия плюс непрерывная архивация WAL-журналов (archive_mode, archive_command). Восстановление накатывает WAL до указанной точки во времени.

PITR мощнее, но и заметно сложнее в эксплуатации: нужно хранить и ротировать WAL, следить за местом, регулярно делать новые базовые копии. Для большинства продуктов разумная стратегия — ежедневный логический бэкап с offsite-копией, а PITR подключать, когда допустимая потеря данных измеряется минутами, а не часами.

Чеклист

  • Бэкап снимается автоматически по расписанию, а не руками.
  • Используется -Fc или -Fd, а не plain-дамп.
  • Глобальные роли сохраняются через pg_dumpall --globals-only.
  • Пароль — в ~/.pgpass с правами 600, не в команде.
  • Старые копии ротируются (find -mtime).
  • Минимум одна копия лежит за пределами сервера с базой.
  • Восстановление проверяется по расписанию, а не теоретически.
  • Дампы с персональными данными шифруются.

Всё перечисленное — это инфраструктура, которую нужно настроить, а потом не забывать обслуживать: следить за местом, за тем, что cron не отвалился, что offsite-выгрузка проходит, что восстановление всё ещё работает.

На Hostim managed-базы (Postgres, MySQL, MongoDB, ClickHouse и другие) поставляются с ежедневными бэкапами и восстановлением одной командой — без cron-скриптов, .pgpass и ручной ротации. Это снимает с проекта целый класс задач, в которых ошибаются именно тогда, когда копия нужнее всего.

Читать дальше