17

基本的なPostgreSQLサーバー構成パラメーターを調整してクエリを最適化する必要があります。work_memドキュメントで私はパラメータに出くわしました。次に、このパラメーターを変更すると、クエリのパフォーマンスにどのように影響するかを確認しました(並べ替えを使用)。さまざまな設定でクエリの実行時間を測定work_memしましたが、非常にがっかりしました。

クエリを実行するテーブルには10,000,000行が含まれており、並べ替えるデータは430MBです。(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

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

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

パフォーマンスが悪化する理由を誰かが説明できますか?または、サーバーパラメータを変更してクエリの実行を高速化する他の方法を提案しますか?

私のクエリ(最適ではないことはわかっていますが、この種のクエリのベンチマークを行う必要があります):

SELECT n
FROM   (
    SELECT n + 1 AS n FROM table_name
    EXCEPT
    SELECT n FROM table_name) AS q1
ORDER BY n DESC;

完全な実行計画:

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
4

2 に答える 2

14

Explain.depesz.comにクエリプランを投稿しました。ご覧ください。

クエリプランナーの見積もりは、場所によってはひどく間違っています。ANALYZE最近走りましたか?

PlannerおよびPlannerのコスト定数で使用される統計に関するマニュアルの章をお読みください。およびの章に特に注意してください。 あなたは試すかもしれません:random_page_costdefault_statistics_target

ALTER TABLE diplomas ALTER COLUMN number SET STATISTICS 1000;
ANALYZE diplomas;

または、1,000万行のテーブルの場合はさらに高くします。これは、データの分散と実際のクエリによって異なります。実験。デフォルトは100、最大は10000です。

そのサイズのデータ​​ベースのwork_mem場合、通常は1MBまたは5MBだけでは不十分です。@alerootがリンクしているTuningPostgresのPostgresWikiページを読んでください。

クエリは出力に応じてディスク上に430104kBのメモリを必要とするため、メモリ内での並べ替えを可能にするには、 500MB以上EXPLAINに設定work_memする必要があります。データのメモリ内表現には、ディスク上表現よりもいくらかのスペースが必要です。TomLaneが最近その件について投稿した内容に興味があるかもしれません。

あなたが試したように、少しだけ増やしwork_memてもあまり役に立たないか、遅くなることさえあります。これをグローバルに高く設定すると、特に同時アクセスの場合、さらに害を及ぼす可能性があります。複数のセッションでリソースが不足する可能性があります。リソースが限られている場合、ある目的にさらに割り当てると、別の目的からメモリが奪われます。最適な設定は、完全な状況によって異なります。

副作用を回避するには、セッション内でローカルに十分な高さに設定し、一時的にクエリ用に設定します。

SET work_mem = '500MB';

後でデフォルトにリセットします。

RESET work_mem;

またはSET LOCAL、現在のトランザクションで開始するためだけに設定するために使用します。

于 2011-11-13T01:09:56.243 に答える
1
SET search_path='tmp';
-- Generate some data ...
-- DROP table tmp.table_name ;
-- CREATE table tmp.table_name ( n INTEGER NOT NULL PRIMARY KEY);
-- INSERT INTO tmp.table_name(n) SELECT generate_series(1,1000);
-- DELETE FROM tmp.table_name WHERE random() < 0.05 ;

例外クエリは、次のNOT EXISTSフォームと同等です。このフォームは、ここで別のクエリプラン(ただし同じ結果)を生成します(9.0.1betaなど)

-- EXPLAIN ANALYZE
WITH q1 AS (
    SELECT 1+tn.n  AS n
    FROM table_name tn
    WHERE NOT EXISTS (
        SELECT * FROM table_name nx
        WHERE nx.n = tn.n+1
        )   
    )
SELECT q1.n
FROM q1
ORDER BY q1.n DESC;

(再帰CTEを備えたバージョンも可能かもしれません:-)

編集:クエリプラン。0.2%が削除された100Kレコードのすべて

元のクエリ:

    ------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=36461.76..36711.20 rows=99778 width=4) (actual time=2682.600..2682.917 rows=222 loops=1)
   Sort Key: q1.n
   Sort Method:  quicksort  Memory: 22kB
   ->  Subquery Scan q1  (cost=24984.41..26979.97 rows=99778 width=4) (actual time=2003.047..2682.036 rows=222 loops=1)
         ->  SetOp Except  (cost=24984.41..25982.19 rows=99778 width=4) (actual time=2003.042..2681.389 rows=222 loops=1)
               ->  Sort  (cost=24984.41..25483.30 rows=199556 width=4) (actual time=2002.584..2368.963 rows=199556 loops=1)
                     Sort Key: "*SELECT* 1".n
                     Sort Method:  external merge  Disk: 3512kB
                     ->  Append  (cost=0.00..5026.57 rows=199556 width=4) (actual time=0.071..1452.838 rows=199556 loops=1)
                           ->  Subquery Scan "*SELECT* 1"  (cost=0.00..2638.01 rows=99778 width=4) (actual time=0.067..470.652 rows=99778 loops=1)
                                 ->  Seq Scan on table_name  (cost=0.00..1640.22 rows=99778 width=4) (actual time=0.063..178.365 rows=99778 loops=1)
                           ->  Subquery Scan "*SELECT* 2"  (cost=0.00..2388.56 rows=99778 width=4) (actual time=0.014..429.224 rows=99778 loops=1)
                                 ->  Seq Scan on table_name  (cost=0.00..1390.78 rows=99778 width=4) (actual time=0.011..143.320 rows=99778 loops=1)
 Total runtime: 2684.840 ms
(14 rows)

存在しません-CTEを使用したバージョン:

----------------------------------------------------------------------------------------------------------------------
 Sort  (cost=6394.60..6394.60 rows=1 width=4) (actual time=699.190..699.498 rows=222 loops=1)
   Sort Key: q1.n
   Sort Method:  quicksort  Memory: 22kB
   CTE q1
     ->  Hash Anti Join  (cost=2980.01..6394.57 rows=1 width=4) (actual time=312.262..697.985 rows=222 loops=1)
           Hash Cond: ((tn.n + 1) = nx.n)
           ->  Seq Scan on table_name tn  (cost=0.00..1390.78 rows=99778 width=4) (actual time=0.013..143.210 rows=99778 loops=1)
           ->  Hash  (cost=1390.78..1390.78 rows=99778 width=4) (actual time=309.923..309.923 rows=99778 loops=1)
                 ->  Seq Scan on table_name nx  (cost=0.00..1390.78 rows=99778 width=4) (actual time=0.007..144.102 rows=99778 loops=1)
   ->  CTE Scan on q1  (cost=0.00..0.02 rows=1 width=4) (actual time=312.270..698.742 rows=222 loops=1)
 Total runtime: 700.040 ms
(11 rows)

存在しません-CTEのないバージョン

--------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=6394.58..6394.58 rows=1 width=4) (actual time=692.313..692.625 rows=222 loops=1)
   Sort Key: ((1 + tn.n))
   Sort Method:  quicksort  Memory: 22kB
   ->  Hash Anti Join  (cost=2980.01..6394.57 rows=1 width=4) (actual time=308.046..691.849 rows=222 loops=1)
         Hash Cond: ((tn.n + 1) = nx.n)
         ->  Seq Scan on table_name tn  (cost=0.00..1390.78 rows=99778 width=4) (actual time=0.014..142.781 rows=99778 loops=1)
         ->  Hash  (cost=1390.78..1390.78 rows=99778 width=4) (actual time=305.732..305.732 rows=99778 loops=1)
               ->  Seq Scan on table_name nx  (cost=0.00..1390.78 rows=99778 width=4) (actual time=0.007..143.783 rows=99778 loops=1)
 Total runtime: 693.139 ms
(9 rows)

私の結論は、「NOT EXISTS」バージョンは、postgresがより良い計画を作成する原因になるということです。

于 2011-11-13T10:58:46.327 に答える