【问题标题】:PostgreSQL 9.4 suddenly invalid memory alloc request sizePostgreSQL 9.4 突然无效的内存分配请求大小
【发布时间】:2025-12-02 05:50:01
【问题描述】:

我正在构建一个网站,该网站将用于处理来自商店的 excel 文件并对其进行操作(合并、查看等)。我正在使用 PostgreSQL 9.4 作为数据库,在具有 4GB RAM 的 Centos 6.6 VM 上运行。它有3个数据库如下:

  • postgres 数据库
  • db_raw,用作数据的占位符。从网站上传的excel将被解析,数据将存储在这里。该数据库由几个表组成,用于保存处理 excel 所需的数据,以及一个用于存储当前 >140 列和近 100 万行的 excel 数据的巨大表
  • db_processed,这是网站的主数据库。它有几个用于网站操作的小表(用户表、访问列表、日志记录等),以及 8 个表来存储来自 db_raw 的处理后的 excel 数据。 8 个表中的每一个都有大约 40 列和大约一百万行。

数据库运行良好,直到今天早上。我尝试通过 pgAdmin 和 PuTTY 连接到 db_processed,PostgreSQL 给了我这个消息

FATAL:  invalid memory alloc request size 144115188075856068

db_raw 工作正常,据我所知,自 3 天前以来没有任何变化。我应该怎么做才能再次连接到数据库?

更新:我按照@CraigRinger 所说的做了并重新启动了服务。我设法连接到数据库,但所有表都不见了:|现在这一直出现在日志中

< 2015-09-21 12:27:22.155 WIB >DEBUG:  performing replication slot checkpoint
< 2015-09-21 12:27:22.158 WIB >LOG:  request to flush past end of generated WAL; request 46/9E0981D8, currpos 46/771C69B0
< 2015-09-21 12:27:22.158 WIB >CONTEXT:  writing block 2 of relation base/18774/12766
< 2015-09-21 12:27:22.158 WIB >ERROR:  xlog flush request 46/9E0981D8 is not satisfied --- flushed only to 46/771C69B0
< 2015-09-21 12:27:22.158 WIB >CONTEXT:  writing block 2 of relation base/18774/12766
< 2015-09-21 12:27:22.158 WIB >WARNING:  could not write block 2 of base/18774/12766
< 2015-09-21 12:27:22.158 WIB >DETAIL:  Multiple failures --- write error might be permanent.

【问题讨论】:

  • o_O?这是完全正确的。请检查 PostgreSQL 错误日志,可能会有更多信息。您可能想在postgresql.conf 中设置log_min_messages = 'debug1',然后在pg_ctl reload 中重新读取配置
  • 顺便说一句,二进制中的数字看起来很可疑:1000000000000000000000000000000000000000000000000011000100

标签: postgresql


【解决方案1】:

这是由损坏的行引起的。

创建一个函数来“检测”损坏的行:

CREATE OR REPLACE FUNCTION is_bad_row(tableName TEXT, tabName TEXT, tidInitial tid)
RETURNS integer 
as $find_bad_row$
BEGIN
    EXECUTE 'SELECT (each(hstore(' || tabName || '))).* FROM ' || tableName || ' WHERE ctid = $1' USING tidInitial; 
    RETURN 0;
    EXCEPTION
        WHEN OTHERS THEN
        RAISE NOTICE '% = %: %', tidInitial, SQLSTATE, SQLERRM;
        RETURN 1;
END
$find_bad_row$
LANGUAGE plpgsql;

...然后创建一个“临时表”来存储坏行的ctid:

create table bad_rows as 
SELECT ctid as row_tid
FROM your_schema.your_table
where is_bad_row('your_schema.your_table', 'your_table', ctid) = 1

...然后你只需要删除这些行:

delete from your_schema.your_table where ctid in (select row_tid from bad_rows)

...并删除“临时表”:

drop table bad_rows

【讨论】:

  • 这是最好的答案。这样您就不必猜测 ctid,如果有多个损坏的行,您也不必重复该过程。
  • 在我们的例子中,我们有一个包含 16M 行的表,其中有一些损坏的行,在执行上述过程(第二个命令)时,系统连接意外关闭,我没有看到任何 n /w,任何地方的内存下降,一切都在本地主机上。我认为一旦找到损坏的行,连接就会关闭。有什么帮助吗?