9

私は〜2TBの完全空白のRedshiftテーブルを持っています。phash(上位カーディナリティ、何億もの値)と複合ソートキー(phash, last_seen)があります。Redshiftクエリの大IN条件を最適化する

私は、クエリは次のように行うと:

SELECT 
    DISTINCT ret_field 
FROM 
    table 
WHERE 
    phash IN (
     '5c8615fa967576019f846b55f11b6e41', 
     '8719c8caa9740bec10f914fc2434ccfd', 
     '9b657c9f6bf7c5bbd04b5baf94e61dae' 
    ) 
AND 
    last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 

それは非常に迅速に返します。しかし、私が10を超えるハッシュ数を増やすと、RedshiftはIN条件を複数のORからまとめて配列に変換します。http://docs.aws.amazon.com/redshift/latest/dg/r_in_condition.html#r_in_condition-optimization-for-large-in-lists

phashの値を持つ場合、「最適化」クエリは次のようになります。 30分を超える秒までの応答時間。言い換えれば、ソートキーの使用を中止し、テーブル全体をスキャンします。

どのように私はこの動作を防ぐことができますし、迅速にクエリを保持するためにsortkeysの使用を保持できますか?

未満10(0.4秒):

XN Unique (cost=0.00..157253450.20 rows=43 width=27) 
    -> XN Seq Scan on table (cost=0.00..157253393.92 rows=22510 width=27) 
       Filter: ((((phash)::text = '394e9a527f93377912cbdcf6789787f1'::text) OR ((phash)::text = '4534f9f8f68cc937f66b50760790c795'::text) OR ((phash)::text = '5c8615fa967576019f846b55f11b6e61'::text) OR ((phash)::text = '5d5743a86b5ff3d60b133c6475e7dce0'::text) OR ((phash)::text = '8719c8caa9740bec10f914fc2434cced'::text) OR ((phash)::text = '9b657c9f6bf7c5bbd04b5baf94e61d9e'::text) OR ((phash)::text = 'd7337d324be519abf6dbfd3612aad0c0'::text) OR ((phash)::text = 'ea43b04ac2f84710dd1f775efcd5ab40'::text)) AND (last_seen >= '2015-10-01 00:00:00'::timestamp without time zone) AND (last_seen <= '2015-10-31 23:59:59'::timestamp without time zone)) 

10以上(45~60分):

ここ

は< 10ハッシュと> 10個の間でハッシュEXPLAINの差であります

XN Unique (cost=0.00..181985241.25 rows=1717530 width=27) 
    -> XN Seq Scan on table (cost=0.00..179718164.48 rows=906830708 width=27) 
       Filter: ((last_seen >= '2015-10-01 00:00:00'::timestamp without time zone) AND (last_seen <= '2015-10-31 23:59:59'::timestamp without time zone) AND ((phash)::text = ANY ('{33b84c5775b6862df965a0e00478840e,394e9a527f93377912cbdcf6789787f1,3d27b96948b6905ffae503d48d75f3d1,4534f9f8f68cc937f66b50760790c795,5a63cd6686f7c7ed07a614e245da60c2,5c8615fa967576019f846b55f11b6e61,5d5743a86b5ff3d60b133c6475e7dce0,8719c8caa9740bec10f914fc2434cced,9b657c9f6bf7c5bbd04b5baf94e61d9e,d7337d324be519abf6dbfd3612aad0c0,dbf4c743832c72e9c8c3cc3b17bfae5f,ea43b04ac2f84710dd1f775efcd5ab40,fb4b83121cad6d23e6da6c7b14d2724c}'::text[]))) 
+0

あなたが言うとき、私は理解していませんよRedshiftは常に完全なテーブルスキャンを行いますが、ソートキーを使用してブロックをスキップすることがあります。クエリの正確な説明を提供できますか? –

+0

問題はありません@MarkHildreth - メインの投稿を編集して、 'EXPLAIN'クエリを追加しました。 – Harry

+0

備考、読者とユーザーにはあまり公平ではありません(ただし、ここで解決策を投稿できます)。postgresqlのパフォーマンスに関する質問のための専用のメーリングリストがあります。 –

答えて

2

sortkeys (last_seen, phash)を設定してみてください。last_seenを先に入力してください。

遅い理由は、ソートキーの先頭の列がランダムな文字のように見えるphashであるためです。 AWS redshift dev docsによれば、タイムスタンプの列は、whereを条件として使用する場合、ソートキーの先頭の列として指定する必要があります。

最近のデータが最も頻繁に照会される場合は、ソートキーの先頭の列としてタイムスタンプ を指定します。 - Choose the Best Sort Key - Amazon Redshift

ソートキーの順で、すべての列が続いphashlast_seenでソートされます。 (What does it mean to have multiple sortkey columns?

ソートキーを変更するには、テーブルを再作成する必要があります。 Thisがそれを行うのに役立ちます。

+0

シンプルなソリューションですが、これで解決しました!それでもまだ高速ではありませんが、ソートキーはランダムな文字列ではひどく非効率です。 – Harry

3

一時テーブル/サブクエリを作成できます。

SELECT DISTINCT t.ret_field 
FROM table t 
JOIN (
    SELECT '5c8615fa967576019f846b55f11b6e41' AS phash 
    UNION ALL 
    SELECT '8719c8caa9740bec10f914fc2434ccfd' AS phash 
    UNION ALL 
    SELECT '9b657c9f6bf7c5bbd04b5baf94e61dae' AS phash 
    -- UNION ALL 
) AS sub 
    ON t.phash = sub.phash 
WHERE t.last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59'; 

代わりに(クエリオプティマイザは、1にそれをマージ中間結果を格納するための補助テーブルを使用している場合)のチャンクで検索します:クエリオプティマイザは1にそれをマージした場合

SELECT ret_field 
FROM table 
WHERE phash IN (
     '5c8615fa967576019f846b55f11b6e41', 
     '8719c8caa9740bec10f914fc2434ccfd', 
     '9b657c9f6bf7c5bbd04b5baf94e61dae') 
    AND last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 
UNION 
SELECT ret_field 
FROM table 
WHERE phash IN () -- more hashes) 
    AND last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 
UNION 
-- ... 

あなたは中間のために一時テーブルを使用しようとすることができます結果

EDIT:

SELECT DISTINCT t.ret_field 
FROM table t 
JOIN (SELECT ... AS phash 
     FROM ... 
) AS sub 
    ON t.phash = sub.phash 
WHERE t.last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59'; 
+0

実際にインデックスを使用し続けています(ありがとう!)が、別のサブクエリから 'phash'値のリストを返す必要があります。手動/コード化されていません。別のサブクエリの結果からUNION ALLを使用/乱用する方法はありますか? :( – Harry

+0

@Harry 'UNION ALL'をファセットを返すもので変更することができます – lad2025

+0

これまでに' EDIT'を試してみたのと同じテーブルスキャン効果があります。 Redshiftから1つの大きなバッチで戻ってきます。 – Harry

2

は、あなたが本当にDISTINCT必要ですか?この演算子は高価な可能性があります。

私はLATERAL JOINを使用しようとしています。下のクエリHashesの列にはphashという列があります。これは大きなバッチのハッシュです。それは、一時テーブル、(サブ)クエリ、何でもかまいません。

SELECT DISTINCT T.ret_field 
FROM 
    Hashes 
    INNER JOIN LATERAL 
    (
     SELECT table.ret_field 
     FROM table 
     WHERE 
      table.phash = Hashes.phash 
      AND table.last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 
    ) AS T ON true 

オプティマイザは、ネストされたループとしてLATERAL JOIN実装していることは非常に可能性があります。それはHashesのすべての行をループし、各行はSELECT FROM tableを実行します。内部SELECTは、(phash, last_seen)にあるインデックスを使用する必要があります。それを安全にプレイするには、ret_fieldをインデックスに含めるだけでなく、それをカバーインデックスにする:(phash, last_seen, ret_field)


@Diegoによって解答には非常に有効なポイントがあります:代わりに、クエリに定数phash値を入れての、一時的または恒久的なテーブルに置きます。

@Diegoで答えを拡張し、ハッシュ付きのこのテーブルがインデックス、ユニークインデックスを持つことが重要であると付け加えたいと思います。

したがってHashesテーブルphashを作成してください。とまったく同じタイプです。タイプが一致することが重要です。その列を固有のクラスタ化インデックスを持つ主キーにします。多くのphash値をHashesテーブルにダンプします。

その後、クエリが単純でINNER JOINなく、横になっ:

SELECT DISTINCT T.ret_field 
FROM 
    Hashes 
    INNER JOIN table ON table.phash = Hashes.phash 
WHERE 
    table.last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 

table(phash, last_seen, ret_field)にインデックスを持つことが重要です。

オプティマイザは、結合された両方のテーブルがphash列でソートされ、Hashesテーブル内で一意であるという事実を利用できるはずです。

+0

私は横方向の結合に対して可能なすべてのバリエーションを試しました。私は継続的に構文エラーを受け取ります。 Redshiftでサポートされていることは確かですか? – Harry

+0

@ハリー、いいえ、私はRedshiftが 'LATERAL JOIN'を持っているかどうかはわかりません。私はPostgresタグを見て、Redshiftタグに注意を払わなかった。不運。 Redshiftにはプロシージャとカーソルが格納されていますか?通常、カーソルは同じことをするときに宣言型SQLよりも遅くなります。しかし、この場合、宣言型SQLは各 'phash'に対して索引探索を行っていないので、結果を一時テーブルに追加する' phash'の明示的なループが全体的に速くなる可能性があります。 –

1

あなたはテンポラリテーブルに必要なデータを挿入し、それをあなたの実際のテーブルに加えることで、 "OR"を取り除くことができます。

select * 
from <my_table> 
where checksum in 
(
'd7360f1b600ae9e895e8b38262cee47936fb6ced', 
'd1606f795152c73558513909cd59a8bc3ad865a8', 
'bb3f6bb3d1a98d35a0f952a53d738ddec5c72c84', 
'b2cad5a92575ed3868ac6e405647c2213eea74a5' 
) 
- ここ

は(しかし、可能な場合は、一時テーブルで行くツールImが使用して複数のSQL文を持って計画を捕捉することは困難であるので、私はCTEを使用しています)の例ですあなたはそれがより複雑に見える見ることができるよう

VERSUS

with foo as 
(
    select 'd7360f1b600ae9e895e8b38262cee47936fb6ced' as my_key union 
    select 'd1606f795152c73558513909cd59a8bc3ad865a8' union 
    select 'bb3f6bb3d1a98d35a0f952a53d738ddec5c72c84' union 
    select 'b2cad5a92575ed3868ac6e405647c2213eea74a5' 
) 
select * 
from <my_table> r 
    join foo f on r.checksum = F.my_key 

、ここでは、計画だが、それはそれは見ていないだろう、なぜならCTEのだという一時テーブル上の方法:

enter image description here

1

すべてのphash値にunionを使用しましたか?ちょうどそのような

:「それはソートキーを使用して停止し、全表スキャンを行う」

SELECT ret_field 
FROM table 
WHERE phash = '5c8615fa967576019f846b55f11b6e41' -- 1st phash value 
and last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 

UNION 

SELECT ret_field 
FROM table 
WHERE phash = '8719c8caa9740bec10f914fc2434ccfd' -- 2nd phash value 
and last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 

UNION 

SELECT ret_field 
FROM table 
WHERE phash = '9b657c9f6bf7c5bbd04b5baf94e61dae' -- 3rd phash value 
and last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59' 

-- and so on... 

UNION 

SELECT ret_field 
FROM table 
WHERE phash = 'nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn' -- Nth phash value 
and last_seen BETWEEN '2015-10-01 00:00:00' AND '2015-10-31 23:59:59'