【问题标题】:Fixing invalid memory alloc request at PostgreSQL 9.2.9修复 PostgreSQL 9.2.9 中无效的内存分配请求
【发布时间】: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


【解决方案1】:

这是对问题中给出的功能的补充,并回答了数据库可转储后的后续步骤。

您的下一步应该是:

  1. 在物理上不同的系统上转储和还原。原因是目前我们不知道是什么原因造成的,而且很有可能是硬件。

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-16
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 2014-11-15
    • 1970-01-01
    相关资源
    最近更新 更多