6

私は次のテーブル/インデックスを持っています-

CREATE TABLE test
(
   coords geography(Point,4326), 
   user_id varchar(50), 
   created_at timestamp
);
CREATE INDEX ix_coords ON test USING GIST (coords);
CREATE INDEX ix_user_id ON test (user_id);
CREATE INDEX ix_created_at ON test (created_at DESC);

これは私が実行したいクエリです:

select * 
from updates 
where ST_DWithin(coords, ST_MakePoint(-126.4, 45.32)::geography, 30000) 
and user_id='3212312' 
order by created_at desc
limit 60

クエリを実行すると、ix_coordsインデックスのみが使用されます。ix_user_idPostgresがクエリにix_created_atもインデックスを使用するようにするにはどうすればよいですか?

これは、本番データの一括挿入を行った新しいテーブルです。testテーブルの合計行数: 15,069,489

私はPostgreSQL9.2.1(Postgisを使用)を(effective_cache_size = 2GB)で実行しています。これは、16GB RAM、Core i7 / 2.5 GHz、非SSDディスクを備えた私のローカルOSXです。

出力の追加EXPLAIN ANALYZE-

Limit  (cost=71.64..71.65 rows=1 width=280) (actual time=1278.652..1278.665 rows=60 loops=1)
   ->  Sort  (cost=71.64..71.65 rows=1 width=280) (actual time=1278.651..1278.662 rows=60 loops=1)
         Sort Key: created_at
         Sort Method: top-N heapsort  Memory: 33kB
         ->  Index Scan using ix_coords on test  (cost=0.00..71.63 rows=1 width=280) (actual time=0.198..1278.227 rows=178 loops=1)
               Index Cond: (coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography)
               Filter: (((user_id)::text = '4f1092000b921a000100015c'::text) AND ('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true))
               Rows Removed by Filter: 3122459
 Total runtime: 1278.701 ms

アップデート:

以下の提案に基づいて、コード+user_idのインデックスを試しました。

CREATE INDEX ix_coords_and_user_id ON updates USING GIST (coords, user_id);

..しかし、次のエラーが発生します。

ERROR:  data type character varying has no default operator class for access method "gist"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.

アップデート:

そこで、CREATE EXTENSION btree_gist;btree/gist複合インデックスの問題を解決しました。そして今、私のインデックスは次のようになります

CREATE INDEX ix_coords_user_id_created_at ON test USING GIST (coords, user_id, created_at);

注:btree_gistはDESC/ASCを受け入れません。

新しいクエリプラン:

Limit  (cost=134.99..135.00 rows=1 width=280) (actual time=273.282..273.292 rows=60 loops=1)
   ->  Sort  (cost=134.99..135.00 rows=1 width=280) (actual time=273.281..273.285 rows=60 loops=1)
         Sort Key: created_at
         Sort Method: quicksort  Memory: 41kB
         ->  Index Scan using ix_updates_coords_user_id_created_at on updates  (cost=0.00..134.98 rows=1 width=280) (actual time=0.406..273.110 rows=115 loops=1)
               Index Cond: ((coords && '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography) AND ((user_id)::text = '4e952bb5b9a77200010019ad'::text))
               Filter: (('0101000020E61000006666666666E63C40C3F5285C8F824440'::geography && _st_expand(coords, 30000::double precision)) AND _st_dwithin(coords, '0101000020E61000006666666666E63C40C3F5285C8F824440'::geography, 30000::double precision, true))
               Rows Removed by Filter: 1
 Total runtime: 273.331 ms

クエリのパフォーマンスは以前よりも向上しており、ほぼ1秒向上していますが、それでも優れていません。これが私が手に入れることができる最高のものだと思いますか?私は60-80msあたりのどこかを望んでいました。またorder by created_at desc、クエリから取得して、さらに100ミリ秒を短縮します。これは、インデックスを使用できないことを意味します。とにかくこれを修正しますか?

4

2 に答える 2

5

PgがGiSTインデックスと通常のbツリーインデックスをビットマップインデックススキャンと組み合わせることができるかどうかはわかりませんが、そうではないと思います。GiSTインデックスに列を追加しなくても、可能な限り最高の結果が得られる可能性がありuser_idます(その結果、を使用しない他のクエリでは、列が大きくなり、遅くなりますuser_id)。

実験として、次のことができます。

CREATE EXTENSION btree_gist;
CREATE INDEX ix_coords_and_user_id ON test USING GIST (coords, user_id);

これは大きなインデックスになる可能性がありますが、それが機能する場合は、そのクエリを後押しする可能性があります。このようなインデックスを維持すると、速度が大幅に低下することに注意してINSERTくださいUPDATE。古いものを削除すると、フィルタリングを行わなくてもix_coordsクエリで使用されますが、より遅くなります。両方を維持すると、との速度低下がさらに悪化します。ix_coords_and_user_iduser_idix_coordsINSERTUPDATE

btree-gistを参照してください


質問を完全に変更する質問への編集によって廃止されました。ユーザーが複数列のインデックスを持っていた場合、ユーザーは2つの別々のインデックスに分割されました):

でフィルタリングや並べ替えを行っているようには見えませuser_idcreate_date。Pgは、のような複数列インデックスの2番目の用語のみを使用しません(できませんか?)(user_id, create_date)。最初の項目も使用する必要があります。

インデックスを作成する場合はcreate_date、別のインデックスを作成します。(user_id, create_date)インデックスを使用して必要とし、通常はuser_id単独で使用しない場合は、列の順序を逆にできるかどうかを確認してください。または、2つの独立したインデックスを作成し (user_id)ます(create_date)。両方の列が必要な場合、Pgはビットマップインデックススキャンを使用して2つの独立したインデックスを組み合わせることができます。

于 2012-10-03T03:03:02.653 に答える
2

クレイグの答えは正しいと思いますが、いくつか追加したかっただけです(コメントには収まりません)。

PostgreSQLにインデックスを使用させるにはかなりの努力が必要です。クエリオプティマイザはスマートであり、シーケンシャルテーブルスキャンの方が高速であると信じられる場合があります。通常は正しいです!:)しかし、インデックスを優先させるために試すことができるいくつかの設定(seq_page_cost、random_page_costなど)で遊ぶことができます。これは、正しい判断を下していないと思われる場合に検討する可能性のあるいくつかの構成へのリンクです。しかし、繰り返しになりますが...私の経験では、ほとんどの場合、Postgresは私よりも賢いです!:)

これがあなた(または将来誰か)に役立つことを願っています。

于 2012-10-03T04:35:41.607 に答える