2012-12-04 13 views
7

PostgreSQL 9.1には2つのテーブルがあります - flight_2012_09_12には約500,000行、position_2012_09_12には約550万行が含まれています。私は単純なジョインクエリを実行しています。これは完了するまでに長い時間がかかります。テーブルが小さいわけではないにもかかわらず、実行時に大きなメリットがあると確信しています。postgresqlクエリを最適化する

クエリは次のとおりです。

SELECT f.departure, f.arrival, 
     p.callsign, p.flightkey, p.time, p.lat, p.lon, p.altitude_ft, p.speed 
FROM position_2012_09_12 AS p 
JOIN flight_2012_09_12 AS f 
    ON p.flightkey = f.flightkey 
WHERE p.lon < 0 
     AND p.time BETWEEN '2012-9-12 0:0:0' AND '2012-9-12 23:0:0' 

分析説明の出力は次のとおりです。

Hash Join (cost=239891.03..470396.82 rows=4790498 width=51) (actual time=29203.830..45777.193 rows=4403717 loops=1) 
Hash Cond: (f.flightkey = p.flightkey) 
-> Seq Scan on flight_2012_09_12 f (cost=0.00..1934.31 rows=70631 width=12) (actual time=0.014..220.494 rows=70631 loops=1) 
-> Hash (cost=158415.97..158415.97 rows=3916885 width=43) (actual time=29201.012..29201.012 rows=3950815 loops=1) 
    Buckets: 2048 Batches: 512 (originally 256) Memory Usage: 1025kB 
    -> Seq Scan on position_2012_09_12 p (cost=0.00..158415.97 rows=3916885 width=43) (actual time=0.006..14630.058 rows=3950815 loops=1) 
      Filter: ((lon < 0::double precision) AND ("time" >= '2012-09-12 00:00:00'::timestamp without time zone) AND ("time" <= '2012-09-12 23:00:00'::timestamp without time zone)) 
Total runtime: 58522.767 ms 

私は問題はポジションテーブルの上に順次スキャンであるが、私は理由を把握することはできませんだと思いますそれはそこにあります。インデックスとテーブル構造は以下の通りです:

   Table "public.flight_2012_09_12" 
    Column  |   Type    | Modifiers 
--------------------+-----------------------------+----------- 
callsign   | character varying(8)  | 
flightkey   | integer      | 
source    | character varying(16)  | 
departure   | character varying(4)  | 
arrival   | character varying(4)  | 
original_etd  | timestamp without time zone | 
original_eta  | timestamp without time zone | 
enroute   | boolean      | 
etd    | timestamp without time zone | 
eta    | timestamp without time zone | 
equipment   | character varying(6)  | 
diverted   | timestamp without time zone | 
time    | timestamp without time zone | 
lat    | double precision   | 
lon    | double precision   | 
altitude   | character varying(7)  | 
altitude_ft  | integer      | 
speed    | character varying(4)  | 
asdi_acid   | character varying(4)  | 
enroute_eta  | timestamp without time zone | 
enroute_eta_source | character varying(1)  | 
Indexes: 
"flight_2012_09_12_flightkey_idx" btree (flightkey) 
"idx_2012_09_12_altitude_ft" btree (altitude_ft) 
"idx_2012_09_12_arrival" btree (arrival) 
"idx_2012_09_12_callsign" btree (callsign) 
"idx_2012_09_12_departure" btree (departure) 
"idx_2012_09_12_diverted" btree (diverted) 
"idx_2012_09_12_enroute_eta" btree (enroute_eta) 
"idx_2012_09_12_equipment" btree (equipment) 
"idx_2012_09_12_etd" btree (etd) 
"idx_2012_09_12_lat" btree (lat) 
"idx_2012_09_12_lon" btree (lon) 
"idx_2012_09_12_original_eta" btree (original_eta) 
"idx_2012_09_12_original_etd" btree (original_etd) 
"idx_2012_09_12_speed" btree (speed) 
"idx_2012_09_12_time" btree ("time") 

      Table "public.position_2012_09_12" 
Column |   Type    | Modifiers 
-------------+-----------------------------+----------- 
callsign | character varying(8)  | 
flightkey | integer      | 
time  | timestamp without time zone | 
lat   | double precision   | 
lon   | double precision   | 
altitude | character varying(7)  | 
altitude_ft | integer      | 
course  | integer      | 
speed  | character varying(4)  | 
trackerkey | integer      | 
the_geom | geometry     | 
Indexes: 
"index_2012_09_12_altitude_ft" btree (altitude_ft) 
"index_2012_09_12_callsign" btree (callsign) 
"index_2012_09_12_course" btree (course) 
"index_2012_09_12_flightkey" btree (flightkey) 
"index_2012_09_12_speed" btree (speed) 
"index_2012_09_12_time" btree ("time") 
"position_2012_09_12_flightkey_idx" btree (flightkey) 
"test_index" btree (lon) 
"test_index_lat" btree (lat) 

私は、クエリを書き換えるため、他の方法を考えることはできませんので、私はこの時点で困惑します。現在の設定がそれと同じくらい良い場合は、現在の設定よりもはるかに速くなるはずです。どんな助けでも大歓迎です。

+0

public.position_2012_09_12テーブルの時刻と時刻の列について統計情報を提供できますか?たぶん、lon <0のインデックスが役立つかもしれないが、この条件にマッチする3950815行のポジションテーブルがあります。この表にはさらに多くのデータがありますか? – sufleR

+0

そのテーブルには5563070個の行があります(最初に述べた350万件の代わりに私の投稿を編集しました) – TheOx

+2

どのバージョンのPostgresqlを使用していますか? – plang

答えて

2

シーケンシャルスキャンを取得する理由は、インデックスを使用するよりも少ないディスクページを読み込むと考えているからです。おそらく正しいでしょう。ノンカバリングインデックスを使用する場合は、一致するすべてのインデックスページを読み込む必要があります。基本的に行識別子のリストを出力します。 DBエンジンは、一致する各データページを読み取る必要があります。

あなたのポジションテーブルは、行ごとに71バイトを加え、それに加えて幾何タイプが取るもの(図では16バイトと仮定します)を87バイト使用します。 Postgresページは8192バイトです。したがって、ページあたり約90行あります。

あなたのクエリは、5563070行のうち3950815行、つまり合計の約70%と一致します。データがランダムに分布していると仮定すると、どこのフィルタに関しては、一致する行のないデータページを見つける確率は30%^ 90になります。これは本質的に何もありません。したがって、インデックスの有効性に関係なく、すべてのデータページを読み込む必要があります。とにかくすべてのページを読まなければならない場合は、通常、テーブルスキャンが良いアプローチです。

私がここに出てくるのは、私がカバーしていないインデックスだと言われています。あなた自身の質問に答えることができるインデックスを作成する準備ができていれば、データページをまったく参照しないで、ゲームに戻ってくることができます。

flight_2012_09_12 (flightkey, departure, arrival) 
position_2012_09_12 (filghtkey, time, lon, ...) 
position_2012_09_12 (lon, time, flightkey, ...) 
position_2012_09_12 (time, long, flightkey, ...) 

ここでドットは、選択している残りの列を表しています。ポジションにインデックスの1つだけが必要ですが、どちらが最善であるかを判断するのは難しいです。第1のアプローチは、フィルタリングを行うために第2のインデックス全体を読み取るコストで、事前ソートされたデータに対してマージ結合を可能にすることができる。 2番目と3番目はデータを事前にフィルタリングできますが、ハッシュ結合が必要です。どのくらいのコストがハッシュジョインに含まれているように見えるかは、マージジョインが良い選択肢かもしれません。

問合せでは、行ごとに87バイトのうち52バイトが必要になり、索引にオーバーヘッドが発生するため、索引では索引で終わることはありません。

もう1つのアプローチは、クラスタリングを見て、その「ランダムに分散した」側面を攻撃することです。

+1

フライトテーブルにカバーリングインデックスを追加する価値があるような気がしませんでした。フルスキャンは220msしかかかりません。 –

+0

@DavidAldridgeフェアポイント、両方のテーブルのフライトキーで始まるカバーインデックスを持つと、マージ結合が可能になります。これは、あらかじめソートされたデータのハッシュ結合より高速です。 – Laurence

+0

@DavidAldridgeユーザーはPostgreSQL 9.1を使用していますが、索引のみのスキャン(索引をカバーするようなもの)がないため、問題は解決しません。 –

3

行数の見積もりはかなり妥当なので、これは統計上の問題ではありません。

私が試してみた:

  • あなたが日常lon < 0を検索した場合position_2012_09_12("time") WHERE (lon < 0)におそらくposition_2012_09_12(lon,"time")のインデックスや部分インデックスを作成します。

  • 設定値:random_page_cost、おそらく1.1。 (a)これによって計画が変更され、(b)新しい計画が実際により速い場合に参照してください。 seqscanを避けることがより速くなるかどうかを確認するテスト目的のためにSET enable_seqscan = off;そうであれば、コストパラメータを変更します。

  • このクエリでは、work_memが増加します。 SET work_mem = 10Mまたはそれを実行する前に。

  • まだPostgreSQLを実行していない場合は、最新のPostgreSQLを実行してください。質問には常にPostgreSQLのバージョンを指定してください。 (編集後の更新):あなたは9.1です。それはいいです。 9.2の最大のパフォーマンス改善はインデックスのみのスキャンであり、このクエリのインデックスのみのスキャンで大いに役立つようではありません。

また、列を削除して行を絞り込むことができる場合は、パフォーマンスがやや向上します。それは違いのトンを作ることはありませんが、いくつかを作るでしょう。