6

次のデータベース (postgres) クエリが実行される場合、2 番目の呼び出しははるかに高速です。

オペレーティング システム (Linux) がディスクからデータを取得する必要があるため、最初のクエリは遅いと思います。2 番目のクエリは、ファイルシステム レベルおよび postgres でのキャッシュの恩恵を受けます。

データベースを最適化して、最初の呼び出しで結果をすばやく取得する方法はありますか?

最初の呼び出し(遅い)

foo3_bar_p@BAR-FOO3-Test:~$ psql

foo3_bar_p=# explain analyze SELECT "foo3_beleg"."id", ... FROM "foo3_beleg" WHERE 
foo3_bar_p-# (("foo3_beleg"."id" IN (SELECT beleg_id FROM foo3_text where 
foo3_bar_p(# content @@ 'footown'::tsquery)) AND "foo3_beleg"."belegart_id" IN 
foo3_bar_p(# ('...', ...));
                                                                                             QUERY PLAN                                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=75314.58..121963.20 rows=152 width=135) (actual time=27253.451..88462.165 rows=11 loops=1)
   ->  HashAggregate  (cost=75314.58..75366.87 rows=5229 width=4) (actual time=16087.345..16113.988 rows=17671 loops=1)
         ->  Bitmap Heap Scan on foo3_text  (cost=273.72..75254.67 rows=23964 width=4) (actual time=327.653..16026.787 rows=27405 loops=1)
               Recheck Cond: (content @@ '''footown'''::tsquery)
               ->  Bitmap Index Scan on foo3_text_content_idx  (cost=0.00..267.73 rows=23964 width=0) (actual time=281.909..281.909 rows=27405 loops=1)
                     Index Cond: (content @@ '''footown'''::tsquery)
   ->  Index Scan using foo3_beleg_pkey on foo3_beleg  (cost=0.00..8.90 rows=1 width=135) (actual time=4.092..4.092 rows=0 loops=17671)
         Index Cond: (id = foo3_text.beleg_id)
         Filter: ((belegart_id)::text = ANY ('{...
         Rows Removed by Filter: 1
 Total runtime: 88462.809 ms
(11 rows)

2 回目の呼び出し(高速)

  Nested Loop  (cost=75314.58..121963.20 rows=152 width=135) (actual time=127.569..348.705 rows=11 loops=1)
   ->  HashAggregate  (cost=75314.58..75366.87 rows=5229 width=4) (actual time=114.390..133.131 rows=17671 loops=1)
         ->  Bitmap Heap Scan on foo3_text  (cost=273.72..75254.67 rows=23964 width=4) (actual time=11.961..97.943 rows=27405 loops=1)
               Recheck Cond: (content @@ '''footown'''::tsquery)
               ->  Bitmap Index Scan on foo3_text_content_idx  (cost=0.00..267.73 rows=23964 width=0) (actual time=9.226..9.226 rows=27405 loops=1)
                     Index Cond: (content @@ '''footown'''::tsquery)
   ->  Index Scan using foo3_beleg_pkey on foo3_beleg  (cost=0.00..8.90 rows=1 width=135) (actual time=0.012..0.012 rows=0 loops=17671)
         Index Cond: (id = foo3_text.beleg_id)
         Filter: ((belegart_id)::text = ANY ('...
         Rows Removed by Filter: 1
 Total runtime: 348.833 ms
(11 rows)

foo3_text テーブルのテーブル レイアウト (28M 行)

foo3_egs_p=# \d foo3_text
                                 Table "public.foo3_text"
  Column  |         Type          |                         Modifiers                          
----------+-----------------------+------------------------------------------------------------
 id       | integer               | not null default nextval('foo3_text_id_seq'::regclass)
 beleg_id | integer               | not null
 index_id | character varying(32) | not null
 value    | text                  | not null
 content  | tsvector              | 
Indexes:
    "foo3_text_pkey" PRIMARY KEY, btree (id)
    "foo3_text_index_id_2685e3637668d5e7_uniq" UNIQUE CONSTRAINT, btree (index_id, beleg_id)
    "foo3_text_beleg_id" btree (beleg_id)
    "foo3_text_content_idx" gin (content)
    "foo3_text_index_id" btree (index_id)
    "foo3_text_index_id_like" btree (index_id varchar_pattern_ops)
Foreign-key constraints:
    "beleg_id_refs_id_6e6d40770e71292" FOREIGN KEY (beleg_id) REFERENCES foo3_beleg(id) DEFERRABLE INITIALLY DEFERRED
    "index_id_refs_name_341600137465c2f9" FOREIGN KEY (index_id) REFERENCES foo3_index(name) DEFERRABLE INITIALLY DEFERRED

ハードウェアの変更 (従来のディスクの代わりに SSD) または RAM ディスクが可能です。しかし、現在のハードウェアでもより高速な結果が得られる可能性があります。

バージョン: x86_64-unknown-linux-gnu 上の PostgreSQL 9.1.2

詳細が必要な場合は、コメントを残してください。

4

4 に答える 4

1

クエリを初めて実行すると、postgres はディスクからデータをロードしますが、これは良好なハード ドライブでも低速です。クエリを 2 回目に実行すると、以前に読み込まれたデータが RAM から読み込まれます。これは明らかに高速です。

この問題の解決策は、リレーション データをオペレーティング システムのバッファ キャッシュまたは PostgreSQL バッファ キャッシュにロードすることです。

int8 pg_prewarm(regclass, mode text default 'buffer', fork text default 'main', first_block int8 default null, last_block int8 default null):

最初の引数は、事前に温める関係です。2 番目の引数は、以下でさらに説明するように、使用する予熱方法です。3 番目は、通常はメインである、予熱するリレーション フォークです。4 番目の引数は、プリウォームする最初のブロック番号です (NULL はゼロの同義語として受け入れられます)。5 番目の引数は、プレウォームする最後のブロック番号です (NULL は、リレーションの最後のブロックまでプレウォームすることを意味します)。戻り値は、予熱されたブロックの数です。

3 つの予熱方法が利用できます。prefetch は、サポートされている場合はオペレーティング システムに非同期のプリフェッチ要求を発行し、サポートされていない場合はエラーをスローします。read は、要求された範囲のブロックを読み取ります。プリフェッチとは異なり、これは同期的であり、すべてのプラットフォームとビルドでサポートされていますが、遅くなる可能性があります。buffer は、要求された範囲のブロックをデータベース バッファ キャッシュに読み込みます。

これらの方法のいずれかで、キャッシュできるよりも多くのブロックをプリウォームしようとすると (プリフェッチまたは読み取りを使用している場合は OS によって、またはバッファーを使用している場合は PostgreSQL によって)、より高い番号のブロックが読み取られるにつれて、より低い番号のブロックが削除される可能性があります。また、事前ウォームアップされたデータは、キャッシュのエビクションから特別に保護されていないため、他のシステム アクティビティによって、新しく事前ウォームアップされたブロックが読み取られた直後に追い出される可能性があります。逆に、プレウォーミングはキャッシュから他のデータを削除することもあります。これらの理由から、事前ウォーミングは通常、キャッシュがほとんど空である起動時に最も役立ちます。

ソース

これが役に立てば幸いです!

于 2014-12-04T14:04:15.270 に答える
0

「WHERE x IN」を JOIN に移動すると、パフォーマンスが大幅に向上することがあります。これを試して:

SELECT
  foo3_beleg.id, ...
FROM
  foo3_beleg b INNER JOIN
  foo3_text  t ON (t.beleg_id = b.id AND t.content @@ 'footown'::tsquery)
WHERE 
  foo3_beleg.belegart_id IN ('...', ...);

これは、私の主張を裏付ける再現可能な実験です。

大きな Postgres データベース (3000 万行) ( http://juliusdavies.ca/2013/j.emse/bertillonage/ ) が手元にあるので、それを postgres 9.4beta3 にロードしました。

結果は印象的です。サブセレクト アプローチは、約 20 倍遅くなります。

time  psql myDb < using-in.sql
real    0m17.212s

time  psql myDb < using-join.sql
real    0m0.807s

複製に興味がある人のために、私の理論をテストするために使用した未加工の SQL クエリを次に示します。

このクエリは「SELECT IN」サブクエリを使用しており、20 倍遅くなります (私のラップトップでは最初の実行で 17 秒)。

  -- using-in.sql
  SELECT
    COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1
  FROM
    files INNER JOIN sigs  ON (files.filesha1 = sigs.filesha1)
  WHERE
    sigs.sigsha1re IN (
      SELECT sigsha1re FROM sigs WHERE sigs.sigsha1re like '0347%'
    )  
  GROUP BY
    infilesha1

このクエリは条件をサブクエリから結合基準に移動し、20 倍高速です (私のラップトップでは最初の実行で 0.8 秒)。

  -- using-join.sql
  SELECT
    COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1
  FROM
    files INNER JOIN sigs  ON (
      files.filesha1 = sigs.filesha1 AND sigs.sigsha1re like '0347%'
    )
  GROUP BY
    infilesha1

ps そのデータベースの目的に興味がある場合は、それを使用して、任意の jar ファイルが 2011 年頃の maven リポジトリ内のすべての jar ファイルにどの程度類似しているかを計算できます。

./query.sh lib/commons-codec-1.5.jar | psql myDb

 similarity |                      a = 39 = commons-codec-1.5.jar  (bin2bin)                       
------------+--------------------------------------------------------------------------------------
  1.000     | commons-codec-1.5.jar
  0.447     | commons-codec-1.4.jar
  0.174     | org.apache.sling.auth.form-1.0.2.jar
  0.170     | org.apache.sling.auth.form-1.0.0.jar
  0.142     | jbehave-core-3.0-beta-3.jar
  0.142     | jbehave-core-3.0-beta-4.jar
  0.141     | jbehave-core-3.0-beta-5.jar
  0.141     | jbehave-core-3.0-beta-6.jar
  0.140     | commons-codec-1.2.jar
于 2014-12-03T00:04:49.610 に答える