我最近在查询我的一些表时遇到了问题。当我尝试选择数据时,我得到一个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;
答案 0 :(得分:1)
这是对问题中给出的函数的补充,并且在db可转储之后回答后续步骤。
接下来的步骤应该是:
转储并在物理上不同的系统上恢复。在这一点上,我们不知道是什么造成了这种情况,并且机会不是太糟糕,可能是硬件。
您需要关闭旧系统并对其运行硬件诊断,以查找问题。你真的想知道发生了什么,所以你再也不会碰到它。特别感兴趣的是:
完成备份策略。特别要看PITR(以及相关的实用程序pgbarman)。如果你碰到它,请确保将来可以从类似的情况中恢复。
数据损坏并不会发生。在极少数情况下,它可能是由PostgreSQL中的错误引起的,但在大多数情况下,这是由于您的硬件或您在后端运行的自定义代码造成的。缩小原因并确保可恢复性至关重要。
假设您没有在数据库中运行自定义C代码,很可能是您的数据损坏是由硬件上的某些东西造成的