为什么PostgreSQL在执行长时间运行的INSERT…SELECT…时不保存到磁盘



我正在使用外部数据包装器将一大块数据(在一列上进行非常简单的日期转换(移动到本地数据库。使用Django游标(因为我懒得拿出凭据来创建一个原始的psycopg2游标(,我做了这样的查询(匿名的,删除了几个连接,但在其他方面与原始相同(:

cursor.executemany(
sql.SQL(
"""
INSERT INTO local_table (
foreign_key_id,
other_foreign_key_id,
datetime,
comment
)
SELECT other_local_table.id,
%s,
(object_date + to_timestamp(object_time, 'HH24:MI')::time) at time zone '…',
comment
FROM imported_schema.remote_table
JOIN other_local_table ON other_local_table.code = remote_table.code
"""
),
[(dummy_id,)],
)

然而,本地Postgres服务器总是在一段时间后被OOM杀死。我原以为Postgres会将新行刷新到磁盘以避免内存不足,但据我所知,这并没有发生——/var/lib/docker/volumes/vagrant_postgres_data只增长了几MB,而常驻内存的使用量增长到了GB。本地服务器没有足够的RAM来将整个结果集保存在内存中,所以我需要一个不涉及更昂贵硬件设置的解决方案。

我需要设置类似wal_sync_methodwork_mem的东西才能工作吗?


根据文件,executemany应该是适合该工作的工具:

该函数对于更新数据库的命令非常有用:查询返回的任何结果集都将被丢弃。


在Linux上同时在服务器和Django 2.1上运行Postgres 10.6容器。除了FDW之外,我没有使用任何扩展。


解释计划:

Insert on local_table  (cost=817872.44..818779.47 rows=25915 width=56)
->  Subquery Scan on "*SELECT*"  (cost=817872.44..818779.47 rows=25915 width=56)
->  HashAggregate  (cost=817872.44..818390.74 rows=25915 width=48)
Group Key: other_local_table.id, 1, timezone('…'::text, (remote_table.object_date + (to_timestamp((remote_table.object_time)::text, 'HH24:MI'::text))::time without time zone)), remote_table.comment
->  Nested Loop  (cost=101.15..807974.88 rows=989756 width=48)
->  Nested Loop  (cost=0.57..60.30 rows=73 width=12)
->  Nested Loop  (cost=0.29..42.35 rows=38 width=4)
->  Seq Scan on fourth_local_table  (cost=0.00..7.45 rows=1 width=4)
Filter: ((code)::text = '…'::text)
->  Index Scan using … on third_local_table  (cost=0.29..34.49 rows=41 width=8)
Index Cond: (id = fourth_local_table.id)
->  Index Scan using … on other_local_table  (cost=0.29..0.45 rows=2 width=16)
Index Cond: (id = third_local_table.id)
->  Foreign Scan on remote_table  (cost=100.58..9421.44 rows=151030 width=20)

postgresqltuner建议I

在/etc/sysctl.conf中设置vm.covercommit_memory=2…这将禁用内存过度commission并避免postgresql被OOM杀手杀死。

这就是解决方案吗?

我在您的执行计划中没有看到除了HashAggregate之外的任何内容,它可能会消耗任何内存量,并且应该受到work_mem的限制。

要对此进行诊断,您应该首先配置您的系统,以便获得常规OOM错误,而不是调用OOM杀手。这意味着用sysctl设置vm.overcommit_memory = 2,并将vm_overcommit_ratio调整为100 * (RAM - swap) / RAM

当服务器出现OOM错误时,它会将当前内存上下文及其大小转储到PostgreSQL日志中。这应该指示内存的去向。如果有疑问,请将其添加到问题中。

您是否使用任何第三方扩展?

相关内容

  • 没有找到相关文章

最新更新