Linux上PostgreSQL中的配置参数work_mem

Grz*_*zes 16 postgresql server-configuration postgresql-performance

我必须通过调整基本的PostgreSQL服务器配置参数来优化查询.在文档中,我遇到了work_mem参数.然后我检查了如何更改此参数会影响我的查询的性能(使用sort).我用各种work_mem设置测量了查询执行时间,非常失望.

我执行查询的表包含10,000,000行,并且有430 MB的数据要排序.(Sort Method: external merge Disk: 430112kB).

work_mem = 1MB,EXPLAIN输出是:

Total runtime: 29950.571 ms (sort takes about 19300 ms).
Sort  (cost=4032588.78..4082588.66 rows=19999954 width=8) 
(actual time=22577.149..26424.951 rows=20000000 loops=1)
                 Sort Key: "*SELECT* 1".n
                 Sort Method:  external merge  Disk: 430104kB
Run Code Online (Sandbox Code Playgroud)

work_mem = 5MB:

Total runtime: 36282.729 ms (sort: 25400 ms).
Sort  (cost=3485713.78..3535713.66 rows=19999954 width=8) 
      (actual time=25062.383..33246.561 rows=20000000 loops=1)
      Sort Key: "*SELECT* 1".n
      Sort Method:  external merge  Disk: 430104kB
Run Code Online (Sandbox Code Playgroud)

work_mem = 64MB:

Total runtime: 42566.538 ms (sort: 31000 ms).
Sort  (cost=3212276.28..3262276.16 rows=19999954 width=8) 
(actual time=28599.611..39454.279 rows=20000000 loops=1)
                 Sort Key: "*SELECT* 1".n
                 Sort Method:  external merge  Disk: 430104kB
Run Code Online (Sandbox Code Playgroud)

谁能解释为什么性能会变差?或者建议任何其他方法通过更改服务器参数来加快查询执行速度?

我的查询(我知道它不是最优的,但我必须对这种查询进行基准测试):

SELECT n
FROM   (
    SELECT n + 1 AS n FROM table_name
    EXCEPT
    SELECT n FROM table_name) AS q1
ORDER BY n DESC;
Run Code Online (Sandbox Code Playgroud)

完整的执行计划:

Sort  (cost=5805421.81..5830421.75 rows=9999977 width=8) (actual time=30405.682..30405.682 rows=1 loops=1)
Sort Key: q1.n
Sort Method:  quicksort  Memory: 25kB
->  Subquery Scan q1  (cost=4032588.78..4232588.32 rows=9999977 width=8) (actual time=30405.636..30405.637 rows=1 loops=1)
    ->  SetOp Except  (cost=4032588.78..4132588.55 rows=9999977 width=8) (actual time=30405.634..30405.634 rows=1 loops=1)
           ->  Sort  (cost=4032588.78..4082588.66 rows=19999954 width=8) (actual time=23046.478..27733.020 rows=20000000 loops=1)
                 Sort Key: "*SELECT* 1".n
                 Sort Method:  external merge  Disk: 430104kB
                 ->  Append  (cost=0.00..513495.02 rows=19999954 width=8) (actual time=0.040..8191.185 rows=20000000 loops=1)
                       ->  Subquery Scan "*SELECT* 1"  (cost=0.00..269247.48 rows=9999977 width=8) (actual time=0.039..3651.506 rows=10000000 loops=1)
                             ->  Seq Scan on table_name  (cost=0.00..169247.71 rows=9999977 width=8) (actual time=0.038..2258.323 rows=10000000 loops=1)
                       ->  Subquery Scan "*SELECT* 2"  (cost=0.00..244247.54 rows=9999977 width=8) (actual time=0.008..2697.546 rows=10000000 loops=1)
                             ->  Seq Scan on table_name  (cost=0.00..144247.77 rows=9999977 width=8) (actual time=0.006..1079.561 rows=10000000 loops=1)
Total runtime: 30496.100 ms
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 13

我在explain.depesz.com上发布了您的查询计划,看一看.

在某些地方,查询计划者的估计是非常错误的.你ANALYZE最近跑过吗?

阅读规划师规划师成本常量使用的统计手册中的章节.要特别注意的章节random_page_costdefault_statistics_target.
我会尝试:

ALTER TABLE diplomas ALTER COLUMN number SET STATISTICS 1000;
ANALYZE diplomas;
Run Code Online (Sandbox Code Playgroud)

鉴于您的1000万行,您甚至可能会更高.这取决于您的数据分布和实际查询.实验.默认值为100,最大值 是10000.

对于该大小的数据库,work_mem通常只有1或5 MB的数据库是不够的.阅读@aleroot链接到的Tuning Postgres上Postgres Wiki页面.

由于您的查询根据输出需要430104kB的内存EXPLAIN,您必须设置work_mem500MB或更多以允许内存中排序.内存中的数据表示比磁盘上的表示需要更多的空间.您可能对Tom Lane最近发布的内容感兴趣.

work_mem如你所尝试的那样增加一点点,显然不会有太大帮助,甚至可能会放慢速度.不知道如何解释减速.也许附加功能SET LOCAL根本没有使用,或者只是最初使用,还不够,但是从其他内存分配中消失了.

如果您的记忆力有限,请不要设置ANALYZE太高,否则可能会使竞争资源匮乏.仅为此查询临时设置足够高:

SET work_mem = '500MB';
Run Code Online (Sandbox Code Playgroud)

之后将其重置为默认值:

RESET work_mem;
Run Code Online (Sandbox Code Playgroud)

或者,更好的是,使用random_page_cost它来为当前事务设置它.无论哪种方式,最好在单个事务中运行所有这些.