PosgreSQL:设置高 work_mem 不会避免磁盘合并

cli*_*ime 5 postgresql

这不是我使用 postgres 的一天。在我使用 PosgreSQL 9.2.3 的服务器机器上,我将 work_mem 设置为 4MB 以避免Sort Method: external merge Disk: 2072kB但它没有帮助:

cwu=# vacuum analyze web_city;
VACUUM
cwu=# SHOW work_mem;
 work_mem 
----------
 4MB
(1 row)
cwu=# explain analyze select count(*) from web_city GROUP BY (left(name,5));
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=18304.35..20487.34 rows=95562 width=10) (actual time=1557.871..1809.029 rows=64459 loops=1)
   ->  Sort  (cost=18304.35..18633.84 rows=131796 width=10) (actual time=1557.856..1707.069 rows=131796 loops=1)
         Sort Key: ("left"((name)::text, 5))
         Sort Method: external merge  Disk: 2072kB
         ->  Seq Scan on web_city  (cost=0.00..4842.45 rows=131796 width=10) (actual time=1.050..174.907 rows=131796 loops=1)
 Total runtime: 1828.936 ms
(6 rows)
Run Code Online (Sandbox Code Playgroud)

设置work_mem8MBfinally 有帮助:

cwu=# SET work_mem = '8MB';
SET
cwu=# explain analyze select count(*) from web_city GROUP BY (left(name,5));
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=5501.43..6675.72 rows=93943 width=10) (actual time=207.628..244.667 rows=64459 loops=1)
   ->  Seq Scan on web_city  (cost=0.00..4842.45 rows=131796 width=10) (actual time=0.749..102.511 rows=131796 loops=1)
 Total runtime: 263.154 ms
(3 rows)
Run Code Online (Sandbox Code Playgroud)

但为什么 4MB 还不够?在postgres wiki 中,有这样一条注释:

如果您在其中看到类似“排序方法:外部合并磁盘:7526kB”这样的行,您就会知道至少 8MB 的 work_mem 通过在 RAM 中排序而不是交换到磁盘来真正提高查询执行的速度。

所以我认为在我的情况下也是一样的。

编辑:如果我这样做:

cwu=# create index name_left_prefix on web_city(left(name, 5));
Run Code Online (Sandbox Code Playgroud)

那么4MB终于够了。似乎索引导致内存使用率降低。如果有人愿意解释所有这些行为,我将不胜感激。

小智 5

这有点推测,但 Depesz (Hubert Lubaczewski)在这个问题上有这样的说法

不过,您可能想知道,为什么 PostgreSQL 在仅使用 448kB 的情况下切换到磁盘?毕竟,work_mem 是 1MB。答案很简单——据我所知——当 work_mem 不够用时会使用磁盘,所以这意味着它已经被填满了。因此,使用“磁盘:448kB”排序意味着或多或少地使用了整个 work_mem 加上 448kB 的磁盘。

因此,在您的情况下,使用的 work_mem 可能在 6 MB 范围内。另外,请先尝试reset work_mem,也许那里有上一个查询中的内容。

  • 只是为了澄清,`RESET work_mem` 所做的是将 `work_mem* *setting* 的 *value* 重置为其默认值,撤消任何先前的 `SET work_mem`。它*不*在任何意义上“清除”或“重置”为`work_mem`分配的内存内容;无论如何,在每个语句的末尾都会自动丢弃此内存。另外:Pg 在估计已经分配了多少 `work_mem` 方面不是很聪明;你不能依赖 `work_mem` 实际上限制查询使用的总工作内存。 (9认同)