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

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

我最近在查询我的一些表时遇到了问题。当我尝试选择数据时,我收到一条错误提示:错误:无效内存分配请求大小 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,而是确切的不成功的(或从预先不成功的计算下一个)所以我可以插入它进入 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. 在物理上不同的系统上转储和恢复。目前我们还不知道是什么原因造成的,很可能是硬件问题。

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

    • 仔细检查 ECC RAM 和 MCE 日志
    • 查看所有 RAID 阵列及其备用电池
    • CPU 和 PSU
    • 如果是我,我还会查看环境变量,例如空调输入和数据中心温度。
  3. 检查您的备份策略。特别是查看 PITR(和相关实用程序 pgbarman)。确保您将来可以从类似情况中恢复过来。

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

假设您没有在数据库中运行自定义 C 代码,您的数据损坏很可能是由于硬件问题造成的

关于postgresql - 修复 PostgreSQL 9.2.9 中的无效内存分配请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36031484/

相关文章:

mysql - 仅从 ibdata1 文件恢复数据

sql - 按每个项目至少有一个进行分组

postgresql - 在 PostgreSQL RDS 上创建表空间

sql - 如何列出与特定表相关的所有表名

arrays - plpgsql text[] varchar[] 数组不工作

postgresql - 两个表之间的交集间隔日期

mysql - 没有数据备份,没有bin-log的mysql数据如何恢复?

hadoop - 如何撤消hadoop fs -rmr?

java - jOOQ "IN"具有 N 次元组的谓词

postgresql - 将 MySQL GROUP BY 转换为 Postgres