web-dev-qa-db-ja.com

個別の選択を高速化する方法は?

私はいくつかの時系列データで単純な選択を区別しています:

SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';

そしてそれは112秒かかります。クエリプランは次のとおりです。

http://explain.depesz.com/s/NTyA

私のアプリケーションは、多くの異なる操作を実行する必要があり、このようにカウントします。この種のデータを取得するより速い方法はありますか?

16
Sam

あなたはおそらくこれを聞きたくないでしょうが、SELECT DISTINCTを高速化する最良のオプションはavoidDISTINCT toで始まる。多くの場合(すべてではありません!)、より良いデータベース設計またはより良いクエリで回避できます。

場合によっては、GROUP BYの方が高速です。これは、コードパスが異なるためです。

あなたの特定のケースでは、DISTINCTを取り除くことができないようです。ただし、そのようなクエリが多数ある場合は、専用のインデックスを使用してクエリをサポートできます。

CREATE INDEX foo ON events (project_id, "time", user_id);

user_idの追加は、これから index-only scans を取得した場合にのみ役立ちます。詳細はリンク先をご覧ください。高価なを削除します ビットマップヒープスキャン クエリプランから。クエリ時間の90%を消費します。

EXPLAINの出力から、クエリでは50万件の一致する行から2,491人の異なるユーザーを要約する必要があることがわかります。これは、あなたが何をしても、超高速になることはありませんが、大幅に高速になる可能性があります。

クエリの時間間隔が常に同じである場合、 MATERIALIIZED VIEW フォールディングuser_id per (project_id, <fixed time intervall>)は大いに役立ちます。ただし、時間間隔が変化する可能性はありません。おそらく、少なくとも1時間あたりのユーザー数またはその他の最小時間単位をフォールドでき、それによってかなりのオーバーヘッドを保証するのに十分なパフォーマンスが得られます。

Nitpick:
おそらく、"time"の述語は実際には次のようになります。

AND "time" >= '2015-01-11 8:00:00'
AND "time" <  '2015-02-10 8:00:00';

余談:
識別子としてtimeを使用しないでください。これは、標準SQLでは 予約語 であり、Postgresでは基本型です。

19

これがサムのケースでの私のテストとアーウィンの答えです

drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;

insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date 
limit 10000000

-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;

CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"         

-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;

アーウィン氏は、「これは聞きたくないかもしれませんが、SELECT DISTINCTを高速化するための最良のオプションは、DISTINCTを最初から回避することです。多くの場合(すべてではありません!)、より良いデータベース設計またはより良いクエリで回避できます。 」私は彼が正しいと思います、「もしあれば」「個別、グループ化、順序付け」の使用を避けるべきです。

サムのケースとして状況に遭遇し、サムは月ごとにイベントテーブルのパーティションを使用できると思います。クエリを実行するとデータサイズが減少しますが、上記のクエリの代わりに実行する関数(pl/pgsql)が必要です。関数は、queryを実行するための適切なパーティションを(条件に応じて)見つけます。

2
Luan Huynh