修复PostgreSQL 9.2.9中的无效内存分配请求

时间:2016-03-16 09:28:10

标签: postgresql postgresql-9.2 data-recovery corrupt-data

我最近在查询我的一些表时遇到了问题。当我尝试选择数据时,我得到一个ERROR告诉:ERROR:无效的内存分配请求大小4294967293.这通常表示数据损坏。有一种很好的精确技术可以删除这里描述的损坏的行:https://confluence.atlassian.com/jirakb/invalid-memory-alloc-request-size-440107132.html
但是,由于我有很多损坏的表,这种方法太慢了。所以,我找到了一个很好的函数,它返回上一个成功的ctid: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,但确切的不成功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 个答案:

答案 0 :(得分:1)

这是对问题中给出的函数的补充,并且在db可转储之后回答后续步骤。

接下来的步骤应该是:

  1. 转储并在物理上不同的系统上恢复。在这一点上,我们不知道是什么造成了这种情况,并且机会不是太糟糕,可能是硬件。

  2. 您需要关闭旧系统并对其运行硬件诊断,以查找问题。你真的想知道发生了什么,所以你再也不会碰到它。特别感兴趣的是:

    • 仔细检查ECC RAM和MCE日志
    • 查看所有RAID阵列及其电池备份
    • CPU和PSU
    • 如果是我,我还会查看环境变量,例如AC in和datacenter temperature。
  3. 完成备份策略。特别要看PITR(以及相关的实用程序pgbarman)。如果你碰到它,请确保将来可以从类似的情况中恢复。

  4. 数据损坏并不会发生。在极少数情况下,它可能是由PostgreSQL中的错误引起的,但在大多数情况下,这是由于您的硬件或您在后端运行的自定义代码造成的。缩小原因并确保可恢复性至关重要。

    假设您没有在数据库中运行自定义C代码,很可能是您的数据损坏是由硬件上的某些东西造成的