5

問題:

SELECT new_filename
FROM tmp2_import_lightnings_filenames
WHERE new_filename
    NOT IN (SELECT filename FROM service.import_lightnings_filenames LIMIT 64500)
LIMIT 1;

実行時間: 62 ミリ秒

SELECT new_filename
FROM tmp2_import_lightnings_filenames
WHERE new_filename
    NOT IN (SELECT filename FROM service.import_lightnings_filenames LIMIT 65000)
LIMIT 1;

実行時間: 4.742 秒.

(すべての制限はテスト専用です)。

すごいラグ!そしてそれは指数関数的に増加します。

表:

CREATE TABLE public.tmp2_import_lightnings_filenames (
  new_filename VARCHAR(63) NOT NULL, 
  CONSTRAINT tmp2_import_lightnings_filenames_pkey PRIMARY KEY(new_filename)
) WITHOUT OIDS;

テーブルサイズ: 7304 文字列

データ例:/xml/2012-07-13/01/01-24.xml

CREATE TABLE service.import_lightnings_filenames (
  id SERIAL, 
  filename VARCHAR(63) NOT NULL, 
  imported BOOLEAN DEFAULT false, 
  strokes_num INTEGER, 
  CONSTRAINT import_lightnings_filenames_pkey PRIMARY KEY(id)
) WITHOUT OIDS;

CREATE UNIQUE INDEX import_lightnings_filenames_idx
ON service.import_lightnings_filenames
USING btree (filename COLLATE pg_catalog."default");

テーブルサイズ: 70812 文字列

データ例:44;/xml/2012-05-26/12/12-18.xml;TRUE;NULL

クエリプラン:

 Limit  (cost=0.00..2108.11 rows=1 width=29) (actual time=240.183..240.183 rows=1 loops=1)
   Buffers: shared hit=539, temp written=307
   ->  Seq Scan on tmp2_import_lightnings_filenames  (cost=0.00..7698823.12 rows=3652 width=29) (actual time=240.181..240.181 rows=1 loops=1)
         Filter: (NOT (SubPlan 1))
         Buffers: shared hit=539, temp written=307
         SubPlan 1
           ->  Materialize  (cost=0.00..1946.82 rows=64500 width=29) (actual time=0.009..198.313 rows=64500 loops=1)
                 Buffers: shared hit=538, temp written=307
                 ->  Limit  (cost=0.00..1183.32 rows=64500 width=29) (actual time=0.005..113.196 rows=64500 loops=1)
                       Buffers: shared hit=538
                       ->  Seq Scan on import_lightnings_filenames  (cost=0.00..1299.12 rows=70812 width=29) (actual time=0.004..42.418 rows=64500 loops=1)
                             Buffers: shared hit=538
 Total runtime: 240.982 ms



  Limit  (cost=0.00..2125.03 rows=1 width=29) (actual time=30734.619..30734.619 rows=1 loops=1)
   Buffers: shared hit=547, temp read=112258 written=669
   ->  Seq Scan on tmp2_import_lightnings_filenames  (cost=0.00..7760626.00 rows=3652 width=29) (actual time=30734.617..30734.617 rows=1 loops=1)
         Filter: (NOT (SubPlan 1))
         Buffers: shared hit=547, temp read=112258 written=669
         SubPlan 1
           ->  Materialize  (cost=0.00..1962.49 rows=65000 width=29) (actual time=0.798..42.306 rows=64820 loops=363)
                 Buffers: shared hit=543, temp read=112258 written=669
                 ->  Limit  (cost=0.00..1192.49 rows=65000 width=29) (actual time=0.005..116.110 rows=65000 loops=1)
                       Buffers: shared hit=543
                       ->  Seq Scan on import_lightnings_filenames  (cost=0.00..1299.12 rows=70812 width=29) (actual time=0.003..43.804 rows=65000 loops=1)
                             Buffers: shared hit=543
 Total runtime: 30735.267 ms

私は何を間違っていますか?

4

2 に答える 2

3

パフォーマンス低下の原因は、不足していてwork_memmaterializeステップがディスクへのスワップアウトを開始したことのようです。ここでマニュアルを引用します:

work_mem (整数)
[...] ハッシュ テーブルは、ハッシュ結合、ハッシュ ベースの集計、およびIN サブクエリのハッシュ ベースの処理で使用されます。

鉱山を強調します。クエリの設定を上げてwork_memもう一度実行して、これを確認します。コメントで @a_horse が指定されているので、次のように呼び出して現在のセッションに設定します。

set work_mem = '64MB';

そのためにシステム管理者は必要ありません。セッション内でデフォルトにリセットできます。

reset work_mem;

セッションが終了すると、設定は失われます。設定を変更postgresql.conf(およびリロード) すると、永続的な効果が得られます。

多くの PostgreSQL パッケージは、非常に保守的な設定 (デフォルト 1MB) で出荷されています。ワークロードに大きく依存しますが、一般的に、4 GB 以上のマシンでは 16 MB が最小です。私は 12 GB の RAM を搭載した専用の db サーバーで 64 MB を使用していますが、同時ユーザーはほとんどいません。

セットアップの一般的な調整を行う必要がある場合があります。以下は、PostgreSQL Wiki の一般的なパフォーマンスの最適化に関する指針のリストです。work_memリンクをたどると、チューニングに関する詳細情報も見つかります。


これとは別に、クエリを書き直すと、おそらく速度も向上します。IN大きなリストを持つサブクエリは、PostgreSQL で最も遅い選択になる傾向があります。

左結合 / IS NULL

SELECT new_filename
FROM   tmp2_import_lightnings_filenames t
LEFT   JOIN (
    SELECT filename
    FROM   service.import_lightnings_filenames
    LIMIT  65000
    ) x ON t.new_filename = x.filename 
WHERE  x.filename IS NULL;

存在しません

特に、次の重複を使用すると高速になりservice.import_lightnings_filenamesます。

SELECT new_filename
FROM   tmp2_import_lightnings_filenames t
WHERE  NOT EXISTS (
    SELECT 1
    FROM (
        SELECT filename
        FROM   service.import_lightnings_filenames
        LIMIT  65000
        ) x
    WHERE t.new_filename = x.filename 
    );

CTEと同じです(おそらく速くはありませんが、読みやすいです):

WITH x AS (
    SELECT filename
    FROM   service.import_lightnings_filenames
    LIMIT  65000
    )
SELECT new_filename
FROM   tmp2_import_lightnings_filenames t
WHERE  NOT EXISTS (
    SELECT 1
    FROM   x
    WHERE  t.new_filename = x.filename 
    );
于 2012-07-18T16:42:18.950 に答える
0
-- SET work_mem=20000;
SET random_page_cost=1.1;
SET effective_cache_size=10000000;

1 ~ 20 MB に設定work_memすると、(コアに収まる限り) ハッシュ テーブルが優先されます。これは、小規模から中規模のクエリに効果的です。

低く設定random_page_costすると、クエリ ジェネレーターは必要に応じてインデックス スキャンを優先します。これは、OP の最初のクエリと 2 番目のクエリの間のトリッピング ポイントです。(ただし、インデックス スキャン ステージはスキップされ、seqscan が優先されます) デフォルト値 (=4) は高すぎます)

effective_cache_sizeOS が維持する LRU バッファリングの推定量です。これをできるだけ高く設定します (スワッピングを発生させずに)

于 2012-07-18T18:07:37.307 に答える