Исправление неверного запроса на выделение памяти в PostgreSQL 9.2.9
Недавно я столкнулся с проблемой при запросе некоторых из моих таблиц. Когда я пытаюсь выбрать данные, я получаю сообщение об ОШИБКЕ: ОШИБКА: неверный размер запроса на выделение памяти 4294967293. Обычно это указывает на повреждение данных. Существует хороший и точный способ удаления поврежденных строк, описанный здесь: https://confluence.atlassian.com/jirakb/invalid-memory-alloc-request-size-440107132.html
Но, поскольку у меня много поврежденных таблиц, этот метод слишком медленный. Итак, я нашел хорошую функцию, которая возвращает последнюю успешную ссылку здесь: http://blog.dob.sk/2012/05/19/fixing-pg_dump-invalid-memory-alloc-request-size/
Поиск поврежденной строки при ее использовании происходит немного быстрее, но недостаточно быстро. Я немного изменил его, чтобы хранить все "последние успешные ctid" в другой таблице, и теперь это выглядит так:
CREATE OR REPLACE FUNCTION
find_bad_row(tableName TEXT)
RETURNS void
as $find_bad_row$
DECLARE
result tid;
curs REFCURSOR;
row1 RECORD;
row2 RECORD;
tabName TEXT;
count BIGINT := 0;
BEGIN
DROP TABLE IF EXISTS bad_rows_tbl;
CREATE TABLE bad_rows_tbl (id varchar(255), offs BIGINT);
SELECT reverse(split_part(reverse($1), '.', 1)) INTO tabName;
OPEN curs FOR EXECUTE 'SELECT ctid FROM ' || tableName;
count := 1;
FETCH curs INTO row1;
WHILE row1.ctid IS NOT NULL LOOP
BEGIN
result = row1.ctid;
count := count + 1;
FETCH curs INTO row1;
EXECUTE 'SELECT (each(hstore(' || tabName || '))).* FROM '
|| tableName || ' WHERE ctid = $1' INTO row2
USING row1.ctid;
IF count % 100000 = 0 THEN
RAISE NOTICE 'rows processed: %', count;
END IF;
EXCEPTION
WHEN SQLSTATE 'XX000' THEN
RAISE NOTICE 'LAST CTID: %', result;
EXECUTE 'INSERT INTO bad_rows_tbl VALUES(' || result || ',' || count || ')';
END;
END LOOP;
CLOSE curs;
END
$find_bad_row$
LANGUAGE plpgsql;
Я совершенно новичок в plpgsql, поэтому я застрял со следующим вопросом: как запросить не пре-неудачный ctid, а точно неуспешный (или рассчитать следующий из пре-неудачных), чтобы я мог вставить его в bad_rows_tbl и использовать в качестве аргумента для оператора DELETE дальше?
Надеюсь на помощь...
UPD: функция, которую я закончил
CREATE OR REPLACE FUNCTION
find_bad_row(tableName TEXT)
RETURNS tid[]
as $find_bad_row$
DECLARE
result tid;
curs REFCURSOR;
row1 RECORD;
row2 RECORD;
tabName TEXT;
youNeedMe BOOLEAN = false;
count BIGINT := 0;
arrIter BIGINT := 0;
arr tid[];
BEGIN
CREATE TABLE bad_rows_tbl (id varchar(255), offs BIGINT);
SELECT reverse(split_part(reverse($1), '.', 1)) INTO tabName;
OPEN curs FOR EXECUTE 'SELECT ctid FROM ' || tableName;
count := 1;
FETCH curs INTO row1;
WHILE row1.ctid IS NOT NULL LOOP
BEGIN
result = row1.ctid;
count := count + 1;
IF youNeedMe THEN
arr[arrIter] = result;
arrIter := arrIter + 1;
RAISE NOTICE 'ADDING CTID: %', result;
youNeedMe = FALSE;
END IF;
FETCH curs INTO row1;
EXECUTE 'SELECT (each(hstore(' || tabName || '))).* FROM '
|| tableName || ' WHERE ctid = $1' INTO row2
USING row1.ctid;
IF count % 100000 = 0 THEN
RAISE NOTICE 'rows processed: %', count;
END IF;
EXCEPTION
WHEN SQLSTATE 'XX000' THEN
RAISE NOTICE 'LAST GOOD CTID: %', result;
youNeedMe = TRUE;
END;
END LOOP;
CLOSE curs;
RETURN arr;
END
$find_bad_row$
LANGUAGE plpgsql;
1 ответ
Это является дополнением к функции, приведенной в вопросе, и отвечает на следующие шаги после того, как дб будет сбрасываться.
Ваши следующие шаги должны быть:
сбросить и восстановить в физически другой системе. Причина в том, что на данный момент мы не знаем, что вызвало это, и есть вероятность, что это может быть аппаратно.
Вам нужно вывести из строя старую систему и запустить диагностику оборудования, чтобы найти проблемы. Вы действительно хотите выяснить, что произошло, чтобы не столкнуться с этим снова. Особый интерес:
- Дважды проверьте ECC RAM и MCE журналы
- Посмотрите на все RAID-массивы и их резервные копии батареи
- Процессоры и блоки питания
- Если бы это был я, я бы также посмотрел на переменные среды, такие как переменный ток и температура в центре обработки данных.
Пройдите свою резервную стратегию. В частности посмотрите на PITR (и связанную с ним утилиту pgbarman). Убедитесь, что вы можете выйти из подобной ситуации в будущем, если столкнетесь с ней.
Повреждение данных не просто происходит. В редких случаях это может быть вызвано ошибками в PostgreSQL, но в большинстве случаев это происходит из-за вашего оборудования или из-за пользовательского кода, который вы запускаете в бэкэнде. Сужение причины и обеспечение возможности восстановления имеют решающее значение в будущем.
Предполагая, что вы не используете пользовательский код C в своей базе данных, скорее всего, вы повредили данные из-за чего-то на оборудовании