Исправление неверного запроса на выделение памяти в 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 ответ

Это является дополнением к функции, приведенной в вопросе, и отвечает на следующие шаги после того, как дб будет сбрасываться.

Ваши следующие шаги должны быть:

  1. сбросить и восстановить в физически другой системе. Причина в том, что на данный момент мы не знаем, что вызвало это, и есть вероятность, что это может быть аппаратно.

  2. Вам нужно вывести из строя старую систему и запустить диагностику оборудования, чтобы найти проблемы. Вы действительно хотите выяснить, что произошло, чтобы не столкнуться с этим снова. Особый интерес:

    • Дважды проверьте ECC RAM и MCE журналы
    • Посмотрите на все RAID-массивы и их резервные копии батареи
    • Процессоры и блоки питания
    • Если бы это был я, я бы также посмотрел на переменные среды, такие как переменный ток и температура в центре обработки данных.
  3. Пройдите свою резервную стратегию. В частности посмотрите на PITR (и связанную с ним утилиту pgbarman). Убедитесь, что вы можете выйти из подобной ситуации в будущем, если столкнетесь с ней.

Повреждение данных не просто происходит. В редких случаях это может быть вызвано ошибками в PostgreSQL, но в большинстве случаев это происходит из-за вашего оборудования или из-за пользовательского кода, который вы запускаете в бэкэнде. Сужение причины и обеспечение возможности восстановления имеют решающее значение в будущем.

Предполагая, что вы не используете пользовательский код C в своей базе данных, скорее всего, вы повредили данные из-за чего-то на оборудовании

Другие вопросы по тегам