2017-12-14 22 views
1

〜150Mのレコードテーブルから一意の数値IDで識別される1,000〜50,000のレコードを取得する必要があります。 AWS RDSでデータベースをホストしています。テーブルには、複数のintegerカラムがあり、idカラムにはcharacter varying(500)bigintの1つがあります。すべての列はbtreeのインデックスを持ちます。Postgres:IN vs JOINを使用して多数の行を選択します。

現在の生産クエリは、これは許容されるM < 1,000 1秒、下に返し

SELECT * 
FROM mytable t 
WHERE id IN (N1, N2, .. Nm) 

あります。問題はmが直線的に増加することである。クエリはm = 30,000の場合、20秒以上かかります。

インデックス付きのテンポラリテーブルを作成し、INNER JOINを使用してパフォーマンスを大幅に向上させようとしました。 (https://stackoverflow.com/a/24647700/226960

ここでは、m> 70kの略式ダンプがあります。

CREATE TEMPORARY TABLE temp_phrases (phrase_id integer) ON COMMIT DROP 
CREATE TABLE temp_phrases: OK, time: 0.01 seconds. 
CREATE INDEX temp_phrases_phrase_id_idx ON temp_phrases(phrase_id) 
CREATE INDEX '.temp_phrases.'_phrase_id_idx: OK, time: 0 seconds. 
INSERT INTO TABLE temp_phrases: 70544, time: 0.3 seconds. 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
EXPLAIN SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
Nested Loop Left Join (cost=0.57..665368.61 rows=79815 width=34) 
-> Seq Scan on temp_phrases (cost=0.00..1111.15 rows=79815 width=4) 
-> Index Scan using thesaurus_pkey on thesaurus (cost=0.57..8.31 rows=1 width=42) 
    Index Cond: (id = temp_phrases.phrase_id) 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
TEMP TABLE AND JOIN: 70544 results, 52.2seconds 

これは、ハードウェア関連のボトルネック
https://stackoverflow.com/a/24254825/226960

それが元id IN(_list_)クエリを向上させることが可能であることを示すだろう、私たちはクエリを繰り返した場合の結果を得るために、1秒未満を取りますか?追加のRDS IOPSが役立つでしょうか?

EDIT
EXPLAIN (ANALYZE, BUFFERS)出力B-Treeインデックス、またはまったくインデックスを使用して

INSERT INTO TABLE temp_phrases: 41504, time: 0.17 seconds. 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
Nested Loop Left Join (cost=0.57..396319.90 rows=46920 width=34) (actual time=0.708..23874.200 rows=41504 loops=1) 
    Buffers: shared hit=167593 read=39458 dirtied=138, local hit=184 
    -> Seq Scan on temp_phrases (cost=0.00..653.20 rows=46920 width=4) (actual time=0.012..21.138 rows=41504 loops=1) 
     Buffers: local hit=184 
    -> Index Scan using thesaurus_pkey on thesaurus (cost=0.57..8.42 rows=1 width=42) (actual time=0.569..0.572 rows=1 loops=41504) 
     Index Cond: (id = temp_phrases.phrase_id) 
     Buffers: shared hit=167593 read=39458 dirtied=138 
Planning time: 1.493 ms 
Execution time: 23887.493 ms 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
SELECT * FROM temp_phrases LEFT JOIN thesaurus ON thesaurus.id = temp_phrases.phrase_id 
TEMP TABLE AND JOIN: 41504 results, 24.2seconds 
+0

「IN」ではなく「EXISTS」を試してください。私はあなたがより良いパフォーマンスを得ると信じています。 –

+1

**あなたの質問に** [編集] **してください**を使用して生成された実行計画を追加する '説明(分析、バッファ)' **。 [**フォーマットされたテキスト**](http://stackoverflow.com/help/formatting)、[スクリーンショットなし](http://meta.stackoverflow.com/questions/285551/why-may-i-not -Upload-images-of-code-on-so-ask-a-question/285557#285557) –

+0

INSERT INTO temp_phrasesの後に(VACUUM)ANALYZEを実行します。これにより統計が更新され、索引計画が作成される可能性があります。 – joop

答えて

0

は、ちょうどそれが行のすべての単一の組み合わせに一致するように試みるようになります。

ただし、試合後に停止させると、2倍のスピードが得られます。

テンポラリテーブル上の一意のキーまたは主キー。あなたがそれに参加すると、1つのマッチが見つかると、他のマッチは見つからないことがわかります。

私の経験では、Aから選択し、Bに参加すると、一意のキーに参加するのに多くの手助けをします。パフォーマンスの向上は通常、単純なインデックスと比較して50%少ない時間です。

他の状況では一意のキーを使用できない場合、ハッシュインデックスは同じ処理を行いますが、インデックスにはしばらく時間がかかります。

プランでわかるように、シーケンススキャンは実行され、一時テーブルのインデックススキャンは実行されません。任意のインデックスを使用すると、そこにかなりのタプルがあるので、おそらく助けになるでしょう。

+0

Craig Ringerはここhttps://stackoverflow.com/a/24647700/226960を推奨しているので、一時テーブルにインデックスを作成していますが、強制していません。 a_horse_with_no_nameによれば、postgres(および他のエンジン)は、5-10%を超える行が返された場合、順次使用します。 https://stackoverflow.com/a/5203827/226960 – BojanG

+0

理論的にはそれほど強いわけではありませんが、10Mのテーブルと3MのテーブルBを組み合わせた経験から、100%のテーブルBの行が選択された。結合条件に固有のキーを追加することで、複数のケースで一貫性のある高速化が実現しました。しかし、プランをもっと慎重に見れば、シソーラスのテーブルは、このプランのこのクエリのユニークキーの恩恵を受けるテーブルのようです。あるいは、ハッシュインデックスかもしれません。 – AlexanderMP

関連する問題