Восстанавливать базу обычно приходится в спешке: данные удалены, миграция сломала схему, сервер не загружается. В этот момент легко сделать вторую ошибку — накатить дамп поверх рабочей базы и потерять то, что ещё можно было спасти. Ниже — команды восстановления для PostgreSQL, MySQL, SQLite и DuckDB, порядок действий, который не ухудшает ситуацию, и чек-лист.
Главное правило: сначала в новую базу
Не восстанавливайте поверх рабочей базы. Порядок такой:
- Сохраните текущее состояние, даже если оно испорчено: дамп или копию файла. В нём могут быть данные, появившиеся после последнего бэкапа.
- Восстановите бэкап в новую базу рядом:
app_restoreвместоapp. - Проверьте её: число строк в ключевых таблицах, свежие записи, работу приложения на ней.
- Переключите приложение на новую базу или переименуйте базы.
Так у вас всегда остаётся путь назад, а ошибка в дампе не уничтожает то, что было.
Сначала определите формат бэкапа
gzip -t app.sql.gz && echo "архив цел"
head -c 5 app.dump; echo # PGDMP — custom-формат pg_dump
gunzip -c app.sql.gz | head -n 20 # текстовый SQL: plain-дамп
Plain-дамп PostgreSQL начинается с комментария PostgreSQL database dump, дамп mysqldump — с MySQL dump. Custom-формат pg_dump начинается с сигнатуры PGDMP, directory-формат — это каталог с файлом toc.dat.
Если бэкап лежит в S3, скачайте его на сервер, где будете восстанавливать:
aws s3 cp s3://my-backups/pg/app-2026-09-17.sql.gz . \
--endpoint-url https://storage.yandexcloud.net --region ru-central1
# Selectel: --endpoint-url https://s3.ru-1.storage.selcloud.ru --region ru-1
Как настроить регулярную выгрузку дампов в бакет, описано в статье pg_dump по cron в S3.
PostgreSQL: plain SQL через psql
Plain-дамп (pg_dump -Fp, формат по умолчанию) — это SQL-скрипт. pg_restore его не читает, восстанавливают его через psql:
createdb app_restore
psql -v ON_ERROR_STOP=1 --single-transaction -d app_restore -f app.sql
# сжатый дамп — через конвейер
gunzip -c app.sql.gz | psql -v ON_ERROR_STOP=1 --single-transaction -d app_restore
-v ON_ERROR_STOP=1останавливает восстановление на первой ошибке. Без него psql продолжит выполнять скрипт, и вы получите базу без части таблиц, не заметив этого в длинном выводе.--single-transactionвыполняет весь дамп одной транзакцией: либо база восстановлена целиком, либо не изменилось ничего.
Типичные ошибки plain-восстановления:
role "app" does not exist. В дампе естьALTER ... OWNER TO app. Создайте роли заранее (см. ниже) или делайте дамп с--no-owner --no-privileges— уpsqlтаких флагов нет, это опцииpg_dump.- Дамп сделан с
--clean, но без--if-exists. На пустой базе первые жеDROPупадут с ошибкой. Восстанавливайте в существующую базу или пересоздайте дамп с--clean --if-exists. - Дамп сделан с
--create. В нём естьCREATE DATABASEи\connect, поэтому подключайтесь к служебной базеpostgres: дамп сам создаст базу с исходным именем. Если база с таким именем уже есть,CREATE DATABASEупадёт — такой дамп удобнее восстанавливать на отдельном сервере или в контейнере. - Версии. Восстанавливайте psql той же или более новой версии, чем pg_dump. Свежие выпуски pg_dump (с августа 2025 года) добавляют в plain-дамп мета-команды
\restrict/\unrestrict, которые старый psql не знает. Дамп с новой версии PostgreSQL на старый сервер может не встать из-за неизвестных параметров и синтаксиса.
PostgreSQL: custom и directory через pg_restore
pg_restore --list app.dump | head -n 30 # оглавление архива
createdb app_restore
pg_restore -d app_restore --no-owner --exit-on-error -j 4 app.dump
# directory-формат: тот же вызов, вместо файла — каталог
pg_restore -d app_restore --no-owner -j 4 ./app.dir
--no-ownerсоздаёт объекты от имени пользователя, который запускает восстановление, — удобно, если ролей из исходной базы нет.-j 4восстанавливает данные и индексы в 4 потока. Работает для custom и directory, но не при чтении из stdin и не вместе с--single-transaction.--clean --if-existsнужны, когда вы восстанавливаете в существующую базу: перед созданием объектов удаляются их старые версии. В новую пустую базу эти флаги не нужны.
pg_restore должен быть не старше pg_dump, которым сделан архив: старый pg_restore не прочитает архив нового формата.
Роли и права
Дамп одной базы не содержит ролей — они глобальные для кластера. Их выгружают отдельно:
pg_dumpall --globals-only > globals.sql # роли, хеши паролей, табличные пространства
psql -v ON_ERROR_STOP=1 -d postgres -f globals.sql
В globals.sql есть хеши паролей, храните его так же аккуратно, как сами дампы. Если какие-то роли на целевом сервере уже существуют, ON_ERROR_STOP остановится на CREATE ROLE — уберите его для этого файла и проверьте вывод вручную.
Переключение на восстановленную базу
Самый простой вариант — поменять строку подключения приложения на app_restore. Если имя базы зашито во много мест, переименуйте базы. Для этого к ним не должно быть подключений:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname IN ('app', 'app_restore') AND pid <> pg_backend_pid();
ALTER DATABASE app RENAME TO app_broken;
ALTER DATABASE app_restore RENAME TO app;
После восстановления выполните ANALYZE (или vacuumdb --analyze-in-stages -d app), чтобы планировщик получил статистику, — иначе первые часы запросы могут идти неудачными планами.
Одна таблица из бэкапа
Чаще всего нужна не вся база, а одна таблица: кто-то выполнил DELETE без WHERE.
Из custom-архива:
createdb app_restore
pg_restore -d app_restore --no-owner -t orders app.dump
pg_restore -t восстанавливает только определение таблицы и её данные — без индексов, ограничений и объектов, от которых таблица зависит (схем, типов). Для точного контроля сохраните оглавление pg_restore -l app.dump > toc.txt, оставьте нужные строки и передайте его через -L toc.txt.
Из plain-дампа выдернуть одну таблицу надёжно нельзя — проще восстановить весь дамп во временную базу и перенести строки:
pg_dump -d app_restore -t public.orders --data-only | psql -v ON_ERROR_STOP=1 -d app
Эта команда вставит все строки таблицы и упадёт на дубликатах первичного ключа. Если нужно вернуть только удалённые строки, выгрузите их во временную таблицу и перенесите INSERT ... SELECT ... WHERE NOT EXISTS.
MySQL и MariaDB
mysql -e "CREATE DATABASE app_restore CHARACTER SET utf8mb4"
mysql --default-character-set=utf8mb4 app_restore < app.sql
# сжатый дамп
gunzip -c app.sql.gz | mysql --default-character-set=utf8mb4 app_restore
Логин и пароль держите в ~/.my.cnf с правами 600, а не в командной строке: -pSECRET виден в списке процессов.
На что смотреть:
--databasesи--all-databases. Если дамп сделан с этими флагами, внутри естьCREATE DATABASEиUSE app. Такой дамп восстановится в исходную базуapp, а не вapp_restore, который вы указали в команде. Проверьтеgrep -m 5 -n '^USE' app.sqlдо запуска.- Кодировка. Если дамп в
utf8mb4, а клиент подключается вlatin1, кириллица превратится в мусор без единой ошибки. Явно указывайте--default-character-set. --force. Флаг продолжает восстановление после ошибок. Иногда он нужен, но результатом может стать база без части таблиц. Если используете его, пишите ошибки в лог и читайте этот лог.- GTID. Дамп с сервера, где включён GTID, содержит
SET @@GLOBAL.GTID_PURGED. На сервере с собственной историей GTID это может закончиться ошибкой. Для переноса на другой сервер делайте дамп с--set-gtid-purged=OFF. - Пользователи и гранты в дамп одной базы не входят — создайте их на целевом сервере отдельно.
Одна таблица: восстановите дамп во временную базу на том же сервере и перенесите нужные строки запросом INSERT INTO app.orders SELECT * FROM app_restore.orders WHERE .... Не переливайте таблицу через mysqldump app_restore orders | mysql app: по умолчанию в дампе есть DROP TABLE IF EXISTS, и рабочая таблица будет заменена целиком.
Подробнее о том, как делать такие дампы регулярно, — в статье mysqldump: автоматизация и ротация.
SQLite
SQLite-база — это файл, поэтому восстановление — это замена файла при остановленном приложении:
sudo systemctl stop app
cd /srv/app/data
gunzip -c /backups/app-2026-09-17.db.gz > app.db.restore
sqlite3 app.db.restore "PRAGMA integrity_check;" # должно быть: ok
mkdir -p ../broken && mv app.db app.db-wal app.db-shm ../broken/ 2>/dev/null || true
mv app.db.restore app.db
sudo systemctl start app
Старые -wal и -shm нельзя оставлять рядом с восстановленным файлом: журнал от прежней базы может быть применён к новой и испортить её. Как делать согласованные копии SQLite, — в статье бэкап SQLite без повреждения.
DuckDB
Файл .duckdb восстанавливается так же, как SQLite: остановите все процессы, которые его открывают, уберите в сторону старый файл и app.duckdb.wal, положите копию на место.
Если бэкап сделан через EXPORT DATABASE, это каталог со схемой и данными. Импортируйте его в новый пустой файл:
duckdb /data/app_restore.duckdb -c "IMPORT DATABASE '/backups/export-2026-09-17'"
EXPORT DATABASE + IMPORT DATABASE полезны и при обновлении DuckDB: файл, записанный новой версией, старая версия может не открыть, а экспорт в CSV или Parquet от формата хранения не зависит. Подробности — в статье бэкап DuckDB-файлов.
Сколько это займёт
Точных цифр без вашей базы никто не даст. Ориентиры качественные:
- замена файла SQLite или DuckDB — время на скачивание и копирование;
- plain-дамп PostgreSQL или MySQL выполняется последовательно, а построение индексов и проверка ограничений часто занимают больше времени, чем вставка данных;
- custom или directory с
-jобычно быстрее на многоядерном сервере; - самый медленный этап при первом реальном восстановлении — поиск нужного файла, ключей доступа и команд. Его сокращает только заранее отрепетированный тест.
Замерьте время на тестовом восстановлении и запишите в runbook. Как это автоматизировать, описано в статье как проверить, что бэкап рабочий.
Чек-лист
До:
- остановить запись: приложение, воркеры, cron-задачи;
- сохранить текущее состояние базы, даже испорченное;
- выбрать версию бэкапа: дата, размер, контрольная сумма;
- проверить версии клиента (
psql,pg_restore,mysql) и свободное место на диске.
Во время:
- восстанавливать в новую базу или новый файл;
- использовать
ON_ERROR_STOP=1и--single-transactionдля psql,--exit-on-errorдля pg_restore; - писать вывод в лог:
... 2>&1 | tee restore.log.
После:
- сравнить число строк в ключевых таблицах и свежесть данных (
max(created_at)); - проверить последовательности, права ролей,
ANALYZE; - переключить приложение и проверить его логи;
- не удалять старую базу, пока не убедитесь, что всё работает;
- записать, что пошло не так, и поправить процесс бэкапа.
Если базу испортил ИИ-агент или неудачная миграция, полезно прочитать и что делать, если ИИ стёр базу данных.
Как это сделать в dbsend
dbsend хранит версии файлов и дампов, которые вы загружаете: SQLite и DuckDB-файлы, plain-дампы PostgreSQL и MySQL. Сервис не подключается к вашему серверу БД и не применяет дамп за вас: восстановление — это скачивание нужной версии, а psql или mysql вы запускаете сами.
dbsend log # история версий
dbsend diff <fromSnapshotId> <toSnapshotId> # что изменилось между версиями
dbsend restore <snapshotId> ./restore/app.sql --yes
psql -v ON_ERROR_STOP=1 --single-transaction -d app_restore -f ./restore/app.sql
dbsend restore скачивает файл во временный, сверяет SHA-256 и только потом атомарно переименовывает. Для SQLite дополнительно выполняется PRAGMA integrity_check, а старые -wal/-shm рядом с целевым путём удаляются (с --yes) — бэкапы SQLite можно восстанавливать прямо на место остановленной базы. При ошибке проверки CLI завершается с кодом 6.
Diff между любыми двумя версиями (схема, таблицы, число строк) помогает выбрать версию до инцидента, а не наугад; он доступен на платных тарифах, подробности на странице тарифов. Все команды — в документации CLI.
Частые вопросы
Чем pg_restore отличается от psql при восстановлении?
psql выполняет plain-дамп как обычный SQL-скрипт. pg_restore читает только архивные форматы pg_dump (custom, directory, tar) и умеет восстанавливать выборочно и в несколько потоков. Plain .sql через pg_restore восстановить нельзя.
Можно ли восстановить дамп PostgreSQL на сервер другой версии?
На более новую версию — как правило, да, это обычный способ обновления. На более старую — не гарантируется: дамп может содержать синтаксис и параметры, которых старый сервер не знает. Клиентские утилиты берите той же или более новой версии, чем pg_dump.
Как восстановить только одну таблицу?
Из custom-архива — pg_restore -t имя_таблицы в новую базу. Из plain-дампа — восстановите всё во временную базу и перенесите нужные строки запросом. В MySQL — тоже через временную базу и INSERT ... SELECT.
Нужно ли останавливать приложение?
Для SQLite и DuckDB — обязательно, иначе процесс продолжит писать в старый файл или журнал. Для PostgreSQL и MySQL при восстановлении в новую базу останавливать не нужно; остановка понадобится в момент переключения.
Что делать, если восстановление упало посередине?
При --single-transaction база осталась пустой — исправьте причину и запустите заново. Без неё удалите частично восстановленную базу и начните с чистой: досоздавать недостающие объекты вручную дольше и опаснее.



