选择大量行时的内存使用情况

Jon*_*noB 9 postgresql performance memory query-performance

我正在尝试从命令行使用 转储大表的全部内容pqsl,但遇到了内存使用量上升的问题,直到进程被终止,甚至任何数据被转储之前。

我不明白的是:为什么查询不立即返回结果,并在不耗尽内存的情况下完成?

这是对我正在尝试的确切内容的解释:

我有一张桌子,说:

CREATE TABLE big
(
  id integer,
  rand double precision
)
Run Code Online (Sandbox Code Playgroud)

插入大量行(5000万):

insert into big 
  select generate_series(1, 50000000) AS id, random();
Run Code Online (Sandbox Code Playgroud)

选择每一行的查询计划看起来像(并不奇怪):

$ psql -d big -c "explain select * from big;"

                       QUERY PLAN                           
----------------------------------------------------------------
 Seq Scan on big  (cost=0.00..924326.24 rows=50000124 width=12)
(1 row)
Run Code Online (Sandbox Code Playgroud)

然后我尝试将内容转储到文件:

$ psql -d big -c "select * from big;" > big.dump
Run Code Online (Sandbox Code Playgroud)

正如我上面所说,这个命令在写入任何数据之前失败,似乎是在被操作系统杀死之前占用了越来越多的内存(被“OOM 杀手”)。

注意:我知道我可以pg_dump用来完成类似的事情,但实际上,我的查询比这更复杂 - 具体来说,我想在转储时将每一行编码为 JSON

一些配置细节:

  • postgresql 版本 = 9.3.4
  • 工作内存 = 1MB
  • 共享缓冲区 = 128MB
  • 有效缓存大小 = 128MB

Dan*_*ité 10

默认情况下,结果完全缓存在内存中,原因有两个:

1) 除非使用该-A选项,否则输出行是对齐的,因此在 psql 知道每列的最大长度之前无法开始输出,这意味着访问每一行(除了大量内存之外,这还需要大量时间)。

2) 除非指定 a FETCH_COUNT,否则psql 会PQexec直接在查询上使用同步函数,它会缓冲整个结果集。但是在设置 a 时FETCH_COUNT,它将使用基于游标的方法,连续获取调用并释放或重用每FETCH_COUNT行的客户端缓冲区。

所以一个大的结果集应该通过如下命令获取:

psql -A -t --variable="FETCH_COUNT=10000" \
     -c "select columns from bigtable" \
     > output-file
Run Code Online (Sandbox Code Playgroud)

随着FETCH_COUNT降低,如果行是非常大的,它仍然吃太多的记忆。

-t代表--tuples-only,这抑制了页眉和页脚的输出。