Комментарии 7
Вне самой сути статьи хочу заметить что архивация данных это нечто на уровне подсознательного самосохранения. И если люди не создавали бэкапы то, возможно, присутствовали какие административные или финансовые барьеры. Может руководство не смогло правильно оценить риски.
Специально для лиги лени, и тех кто потерял нить повествования где-то на лирических отступлениях
На конференции PgConf 2026 в Москве Андрей Билле из Postgres Professional рассказал о реальном случае восстановления кластера PostgreSQL (2,5 ТБ, 10 баз 1С), который три года работал без бэкапов на виртуальной машине с переподпиской дискового пространства. Когда при загрузке новой инфобазы закончилось место на диске, администраторы усугубили ситуацию: удалили WAL-файлы, не смогли штатно остановить экземпляр, убили процесс postmaster сигналом SIGKILL, а затем применили pg_resetwal, отключив восстановление — в результате база была серьёзно повреждена. При попытках восстановления выяснилось, что системные индексы разрушены, pg_dump не работал из-за долгих запросов к системному каталогу без индексов, и пришлось создавать специальные патчи. Примечательно, что из зала прозвучал элегантный вопрос: нельзя ли было вместо патчей просто использовать команду COPY для выгрузки данных — докладчик признал, что это было возможно. В итоге удалось частично восстановить лишь одну из баз, а компания-клиент приобрела enterprise-поддержку у Postgres Professional. Главный вывод доклада: нельзя эксплуатировать базы с важными данными без реплик, бэкапов и дампов, не стоит удалять WAL-файлы, убивать процессы PostgreSQL сигналом SIGKILL и применять pg_resetwal без крайней необходимости, а при восстановлении стоит рассматривать простейшие решения прежде, чем модифицировать ядро СУБД.
Помимо необходимости бекапов я бы еще добавил что не надо жопить деньги на инфраструктуру. Нужно мето - купите диски.
Виртуальный диск создан без предварительного резервирования места имеет размер больше, чем физический диск.
Недостаток опыта, думаю. В системном администрировании и работе в виртуальных средах.
Добрый день!
Возможно в микрофон я не смог донести ситуацию целиком, постараюсь текстом:
Команда COPY насколько мне известно и судя по документации работает только для таблицы, т.е. чтобы её использовать нужно знать имя таблиц
Таблиц - тысячи в одной базе, баз 50 +
Т.е. Итого 500+ тысяч таблиц
Казалось бы - ну напишем скрипт, но...
Главная проблема в том, что получить имена таблиц невозможно
Из-за повреждения системных индексов любой запрос уходил в бесконечное выполнение.
При отключении в конфиге использования системных индексов, запрос на получение списка таблиц в одной из десятков баз выполнился только за 2,5 дня!
Такого количества времени не было,счёт шёл на часы...
К сожалению по той же причине (повреждение системных индексов) не работали опции --schema-only и --data-only у pg_dump.
Была надежда что --data-only нас спасёт, так как индексов в разы больше чем таблиц, и соответственно рассчитывали на то, что при этой опции не будет запроса на получение списка и состава индексов и запрос отработает сильно быстрее, но оказалось что pg_dump всё равно запрашивает список и состав индексов даже при указании --data-only.
Поэтому пришлось патчить pg_dump заставив его не запрашивать информацию об индексах.
Затем обрабатывали уже ошибки pg_dump. когда он находил сбойный чанк, пропускали таблицу со сбойным чанком через --exclude-table и её уже спасали через COPY + dblink + offset
В итоге мы получили данные через -data-only + всё что можно было спасти через "copy + dblink + offset", затем процедурой выгрузки/загрузки dt восстановить индексы
Антон, спасибо, за комментарий!
При отключении в конфиге использования системных индексов, запрос на получение списка таблиц в одной из десятков баз выполнился только за 2,5 дня!
pg_dump для получения выполняет сложный select, который Андрей привёл в багрепорте. Без индексов такой запрос, конечно, выполняется долго.
Скрытый текст
SELECT t.tableoid, t.oid, i.indrelid, t.relname AS indexname, t.relpages, t.reltuples, t.relallvisible, 0 AS relallfrozen, pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, i.indkey, i.indisclustered, c.contype, c.conname, c.condeferrable, c.condeferred, c.tableoid AS contableoid, c.oid AS conoid, pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, CASE WHEN i.indexprs IS NOT NULL THEN (SELECT pg_catalog.array_agg(attname ORDER BY attnum) FROM pg_catalog.pg_attribute WHERE attrelid = i.indexrelid) ELSE NULL END AS indattnames, (SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, t.reloptions AS indreloptions, i.indisreplident, inh.inhparent AS parentidx, i.indnkeyatts AS indnkeyatts, i.indnatts AS indnatts, (SELECT pg_catalog.array_agg(attnum ORDER BY attnum) FROM pg_catalog.pg_attribute WHERE attrelid = i.indexrelid AND attstattarget >= 0) AS indstatcols, (SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) FROM pg_catalog.pg_attribute WHERE attrelid = i.indexrelid AND attstattarget >= 0) AS indstatvals, i.indnullsnotdistinct, NULL AS conperiod FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) LEFT JOIN pg_catalog.pg_constraint c ON (i.indrelid = c.conrelid AND i.indexrelid = c.conindid AND c.contype IN (‘p’,‘u’,‘x’)) LEFT JOIN pg_catalog.pg_inherits inh ON (inh.inhrelid = indexrelid) WHERE (i.indisvalid OR t2.relkind = ‘p’) AND i.indisready ORDER BY i.indrelid, indexname;
Участник говорил о том, что стоило попробовать следующие команды и, скорее всего, список схем и таблиц выгрузился бы моментально. Вы и сами написали, что использовали "COPY + dblink + offset ", значит и COPY pg_class бы выгрузил список таблиц.
postgres@host:~$ postgres --single -c ignore_system_indexes=1
PostgreSQL stand-alone backend
backend> copy (select relnamespace, relname from pg_class where relkind='r') to '/tmp/pg_class.txt'
backend> copy (select oid, nspname from pg_namespace) to '/tmp/pg_namespace.txt'
backend>
postgres@host:~$ tail -n 3 /tmp/pg_class.txt
2200 pgbench_branches_pkey
2200 pgbench_tellers_pkey
2200 pgbench_accounts_pkey
postgres@student:~$ tail -n 3 /tmp/pg_namespace.txt
11 pg_catalog
2200 public
13239 information_schemaМожно было даже попробовать не в single mode, просто командой copy pg_class to '/tmp/pg_class.txt';
Но вы не должны были знать, что так можно выгрузить. Это должна знать техподдержка, а её у клиента не было, они её купили уже после.

Если ваш админ — самурай или «обнять и плакать»