【发布时间】:2016-03-16 09:28:09
【问题描述】:
我最近在查询我的一些表时遇到了问题。当我尝试选择数据时,我收到一条错误消息:错误:无效的内存分配请求大小 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;
【问题讨论】:
-
您最初是如何进入这种情况的?从备份还原后的时间点恢复(酒保等)不是一种选择吗?
-
不幸的是,由于 postgresql.conf 中的“archive_mode”命令被注释,我猜,时间点恢复不可用。这就是我尝试修复内容的原因。
-
我修改了一个 quastion 并添加了一个我最终得到的函数。它返回一个损坏行的 tid 数组。它可以用在 DELETE 语句的 WHERE 子句中。我已经使用它删除了多个损坏的行。
-
一旦数据库进入可转储状态,转储它,关闭它,归档旧数据库并重新初始化数据库。不要继续使用它。
-
感谢您的回复!我是否理解正确,我需要从该转储中创建一个新数据库?
标签: postgresql postgresql-9.2 data-recovery corrupt-data