Разворачивание большого SQL-дампа через psql может занять часы. В какой-то момент почти у каждого возникает вопрос: «Оно ещё работает или уже зависло?». В этой статье — команда запуска импорта и набор запросов, которые позволяют мониторить процесс.
Команда запуска импорта
Базовый вариант для дампа, созданного с флагами --inserts --column-inserts:
|
1 2 3 4 |
psql -q -v ON_ERROR_STOP=1 \ -h localhost -U username -p 5433 \ -d database \ -f dump.sql |
Разберём ключи:
| Флаг | Зачем |
|---|---|
-h localhost -U username -p 5433 | Параметры подключения (те же, что при pg_dump) |
-d database | Целевая БД. Должна существовать заранее |
-f dump.sql | Файл с дампом |
-q | Тихий режим: не засорять консоль строками INSERT 0 1 |
-v ON_ERROR_STOP=1 | Прервать при первой ошибке, а не «дожевать» до конца |
Хотите сохранить лог на будущее, но не засорять консоль:
|
1 2 3 4 |
psql -q -v ON_ERROR_STOP=1 \ -h localhost -U username -p 5433 \ -d database -f dump.sql \ > restore.log <strong>2</strong>><strong>&1</strong> |
Консоль чистая, весь вывод — в restore.log.
Диагностика: работает или висит?
Пока импорт идёт, откройте вторую сессию psql (к той же или любой другой БД) и выполните диагностический запрос.
Что делает сервер прямо сейчас
Запросим текущую активность сервера по интересующей нас базе данных:
|
1 2 3 4 5 |
SELECT pid, usename, state, wait_event_type, wait_event, now() - query_start AS duration, left(query, 100) AS query FROM pg_stat_activity WHERE datname = 'database' ORDER BY query_start; |
Что означают поля:
state — состояние сессии:
| Значение | Что означает |
|---|---|
active | Запрос выполняется. Скорее всего, просто долго |
idle in transaction | Транзакция открыта, но ничего не делает. Возможно, psql чего-то ждёт |
idle | Сессия простаивает. Может быть, клиент уже отвалился |
wait_event_type / wait_event — на чём именно процесс ждёт:
| Значение | Что означает |
|---|---|
NULL | Процесс работает, не ждёт. Не завис |
Lock | Ждёт блокировку. Кто-то держит таблицу — самая частая причина «зависания» |
IO (DataFileRead, WALWrite и др.) | Ждёт диск. Работает, просто медленно |
Client | Ждёт данных от клиента. Странно, если вы ничего не вводите |
Timeout, LWLock, BufferPin | Внутренние ожидания, обычно нормальны |
duration — сколько уже длится запрос. Если значение растёт — процесс жив.
Кто кого блокирует
Если в поле wait_event_type стоит Lock — надо найти виновника:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query, blocking.state AS blocking_state FROM pg_stat_activity blocked JOIN pg_locks bl ON bl.pid = blocked.pid AND NOT bl.granted JOIN pg_locks kl ON kl.locktype = bl.locktype AND kl.database IS NOT DISTINCT FROM bl.database AND kl.relation IS NOT DISTINCT FROM bl.relation AND kl.page IS NOT DISTINCT FROM bl.page AND kl.tuple IS NOT DISTINCT FROM bl.tuple AND kl.granted JOIN pg_stat_activity blocking ON blocking.pid = kl.pid WHERE blocked.pid <> blocking.pid; |
В колонке blocking_query будет тот запрос, который держит вашу таблицу. Часто это забытая транзакция (idle in transaction) или другое приложение, забывшее сделать COMMIT.
Быстрый способ посмотреть все неудовлетворённые блокировки:
|
1 |
SELECT * FROM pg_locks WHERE NOT granted; |
Растёт ли прогресс
Для начала подключитесь к нужной базе (консоль psql):
|
1 |
\c database |
Самый надёжный признак, что работа идёт — меняются счётчики. Несколько независимых способов:
Размер базы данных. Повторите запрос дважды с паузой 20–30 секунд:
|
1 |
SELECT pg_size_pretty(pg_database_size('database')); |
Database — это имя вашей базы данных.
Если размер растёт — данные заливаются.
Статистика вставок:
|
1 2 3 |
SELECT sum(n_live_tup) AS live_rows, sum(n_tup_ins) AS inserted_rows FROM pg_stat_user_tables; |
n_live_tup— оценка числа «живых» строк во всех пользовательских таблицах. Растёт по мере заливки данных, но это оценка планировщика, а не точныйcount(*). Обновляется фоновым процессом статистики (autovacuum/stats collector), поэтому может «отставать» на секунды.n_tup_ins— сколько строк вставлено с момента сброса статистики. Это накопительный счётчик, он только растёт и никогда не уменьшается, пока статистику не сбросят.
Именно n_tup_ins — самый полезный для отслеживания прогресса: он монотонно увеличивается, пока идут INSERT-ы.
Статистика собирается асинхронно, с интервалом в несколько секунд. Для мониторинга это нормально, но не ждите мгновенной реакции.
Прямой count(*) по таблице, если знаете, куда льются данные:
|
1 |
SELECT count(*) FROM some_table; |
Алгоритм такой — посмотрели что делает сервер прямо сейчас (увидели что идет команда INSERT в какую то таблицу), а потом можно выполнить пару раз count(*) по этой таблице, чтобы убедиться, что число строк растет.
Типичные причины «зависания»
Сетевые проблемы. Если сервер не локальный, большие потери пакетов и высокий RTT сильно тормозят поштучные INSERT-ы.
Блокировка (lock). Кто-то другой держит таблицу. Самая частая причина. Лечится поиском и завершением блокирующего.
idle in transaction у самого restore-процесса. Дамп открыл транзакцию (BEGIN) и ждёт чего-то, что не приходит.
Медленный формат дампа. --inserts --column-inserts — самый медленный формат восстановления: каждая строка — отдельный INSERT, часто отдельная транзакция, round-trip к серверу на каждую строку.
Нехватка ресурсов. Диск в 100%, CPU на пределе, мало RAM — всё замедляется.
Как ускорить восстановление в будущем
Формат --inserts --column-inserts выбирают, когда важна переносимость дампа (например, для загрузки в другую СУБД). Для обычного бэкапа PostgreSQL он не оптимален. Альтернативы:
COPY(по умолчанию).pg_dumpбез--insertsиспользуетCOPY FROM stdin— в разы быстрее, чем построчныеINSERT.- Custom-формат
-Fc+pg_restore -j 4. Позволяет параллельное восстановление и выборочную загрузку отдельных таблиц. - Обернуть restore в одну транзакцию. Добавьте
BEGIN;в начало дампа иCOMMIT;в конец — коммитов будет один, а не миллион.


