После сбоя диска или другого аппаратного повреждения PostgreSQL может отказаться читать отдельные таблицы. В логе или при запросе к таблице появляется ошибка вида:
ERROR: invalid page in block 1385 of relation base/16394/17647
или по-русски:
ОШИБКА: неверная страница в блоке 1385 отношения base/16394/17647
Как правило, без восстановления из резервной копии остаётся одна повреждённая таблица. Остальная база ClubIS при этом может продолжать работать. Ниже — как определить, какая таблица пострадала, временно «обнулить» битые страницы (если нужно добраться до данных или снять дамп), и восстановить таблицу из резервной копии.
Важно. Операции ниже выполняются от суперпользователя PostgreSQL (
postgres) в pgAdmin илиpsql. Перед началом остановите работу пользователей ClubIS с базой и убедитесь, что есть актуальная резервная копия.
base/…/… в сообщении об ошибкеЧисла в пути base/<oid_базы>/<filenode> означают:
| Часть пути | Что это |
|---|---|
base |
каталог данных PostgreSQL для обычных таблиц |
первое число (16394) |
OID базы данных |
второе число (17647) |
идентификатор файла таблицы на диске (relfilenode) |
Подключитесь к любой служебной базе (например, postgres) и выполните:
SELECT datname
FROM pg_database
WHERE oid = 16394;
Подставьте OID из сообщения об ошибке.
Подключитесь к найденной базе (обычно clubis) и выполните:
SELECT pg_filenode_relation(0, 17647);
Второй аргумент — filenode из пути в ошибке. Первый (0) — tablespace по умолчанию.
zero_damaged_pages)Параметр zero_damaged_pages заставляет PostgreSQL заменить повреждённые страницы нулями вместо немедленного отказа. Данные на этих страницах будут потеряны. Используйте только если таблицу всё равно планируете восстановить из резервной копии, а обнуление нужно, чтобы база открылась или чтобы выполнить последний экспорт.
Ниже — пример для таблицы contracts_history; подставьте имя таблицы, которое нашли на предыдущем шаге.
SELECT count(*) FROM contracts_history;
Ожидаемая ошибка (номер блока и путь могут отличаться):
ERROR: invalid page in block 1385 of relation base/16394/17647
Проверить текущее значение:
SHOW zero_damaged_pages;
Обычно:
off
(1 row)
Включить только для текущей сессии:
SET zero_damaged_pages = on;
Убедиться:
SHOW zero_damaged_pages;
on
(1 row)
SELECT count(*) FROM contracts_history;
PostgreSQL выдаст предупреждение и обнулит повреждённую страницу:
WARNING: invalid page in block 1385 of relation base/16394/17647; zeroing out page
count
--------
97189
(1 row)
Число строк после обнуления может быть меньше, чем до сбоя: часть записей на битых страницах безвозвратно потеряна.
В той же или новой сессии:
SET zero_damaged_pages = off;
Не оставляйте zero_damaged_pages = on в postgresql.conf без крайней необходимости.
После того как повреждённая таблица определена и (при необходимости) база снова доступна для администрирования:
clubis повторно заливать полный дамп нельзя — см. раздел «Восстановление из резервной копии» в той же статье).INSERT … SELECT, pg_restore с ключом -t, или замена таблицы после согласования с техподдержкой ClubIS).Если повреждена критичная для работы клуба таблица и восстановление одной таблицы невозможно, может потребоваться полное восстановление базы в новую базу данных с переключением подключения ClubIS — обратитесь в техподдержку ClubIS.
Эта ошибка означает, что файл таблицы на диске отсутствует (удалён или не восстановился после сбоя), а не только повреждена отдельная страница.
Путь читается так же: base/<oid_базы>/<filenode>. Пример: base/33264/49743 — OID базы 33264, filenode 49743.
SELECT datname
FROM pg_database
WHERE oid = 33264;
В найденной базе:
SELECT pg_filenode_relation(0, 49743);
Если функция не возвращает имя (файл уже удалён из каталога), ориентируйтесь на контекст ошибки в логе PostgreSQL или обратитесь в техподдержку ClubIS.
Обнуление страниц (zero_damaged_pages) при отсутствующем файле не поможет.
serial / sequence)У многих таблиц ClubIS поле id заполняется автоматически через последовательность (sequence, тип serial). После частичного восстановления или обнуления битых страниц данные в таблице могут быть в порядке, а счётчик — указывать на неверное значение или сам быть повреждён.
Имя последовательности обычно совпадает с <имя_таблицы>_id_seq. Узнать точное имя для колонки id:
SELECT pg_get_serial_sequence('contracts_history', 'id');
Подставьте своё имя таблицы.
idЕсли при добавлении записей появляется ошибка о дубликате первичного ключа по id, но сами данные в таблице выглядят корректно:
SELECT setval(
'contracts_history_id_seq',
(SELECT COALESCE(MAX(id), 1) FROM contracts_history)
);
После этого новые записи получат id больше текущего максимума в таблице.
Если ошибки связаны именно с sequence (не удаётся получить следующее значение, повреждён файл счётчика на диске), последовательность пересоздают. Пример для contracts_history — замените имя таблицы и sequence на свои:
ALTER TABLE public.contracts_history ALTER COLUMN id SET DEFAULT 0;
DROP SEQUENCE public.contracts_history_id_seq;
CREATE SEQUENCE public.contracts_history_id_seq;
ALTER SEQUENCE public.contracts_history_id_seq OWNER TO clubis;
ALTER TABLE public.contracts_history
ALTER COLUMN id SET DEFAULT nextval('contracts_history_id_seq'::regclass);
SELECT setval(
'contracts_history_id_seq',
(SELECT COALESCE(MAX(id), 1) FROM contracts_history)
);
Важно. Команды выполняйте от
postgres. Владелец sequence —clubis, как у остальных объектов базы ClubIS. Перед пересозданием sequence согласуйте действие с техподдержкой ClubIS, если не уверены в затронутой таблице.
id — см. раздел Восстановление счётчиков выше.