7

PostgreSQL 9.1 には、約 500,000 行を含む flight_2012_09_12 と約 550 万行を含む position_2012_09_12 の 2 つのテーブルがあります。私は単純な結合クエリを実行していますが、完了するまでに長い時間がかかります。テーブルが小さくないという事実にもかかわらず、実行にはいくつかの大きな利点があると確信しています。

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

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'

Explain Analyst の出力は次のとおりです。

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)

クエリを書き直す他の方法は考えられないので、この時点で困惑しています。現在の設定がうまくいっていればそれでいいのですが、現在よりもはるかに高速になるはずです。どんな助けでも大歓迎です。

4

2 に答える 2

3

行数の見積もりはかなり合理的であるため、これが統計の問題であるとは思えません。

私は試してみたい:

  • を定期的に検索する場合は、 にインデックスを作成するposition_2012_09_12(lon,"time")か、場合によっては部分的なインデックスを作成します。position_2012_09_12("time") WHERE (lon < 0)lon < 0

  • 低く設定するrandom_page_costと、おそらく 1.1 になります。(a) これにより計画が変更されるかどうか、および (b) 新しい計画が実際により高速であるかどうかを確認します。テスト目的で、seqscan を回避する方が速くなるかどうかを確認することができSET enable_seqscan = offます。そうである場合は、コスト パラメータを変更します。

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

  • まだ実行していない場合は、最新の PostgreSQL を実行します。質問では必ず PostgreSQL のバージョンを指定してください。(編集後に更新): 9.1 を使用しています。それはいいです。9.2 での最大のパフォーマンスの向上はインデックスのみのスキャンであり、このクエリのインデックスのみのスキャンから大きなメリットが得られるとは思えません。

列を削除して行を狭めることができれば、パフォーマンスもいくらか向上します。それほど大きな違いはありませんが、多少は変わります。

于 2012-12-04T23:29:06.430 に答える
2

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

位置テーブルは、行ごとに 71 バイトを使用し、geom タイプが取るものは何でも (説明のために 16 バイトと仮定します)、87 バイトになります。Postgres ページは 8192 バイトです。したがって、1 ページあたり約 90 行になります。

クエリは、5563070 行のうち 3950815 行、つまり合計の約 70% に一致します。where フィルターに関して、データがランダムに分散されていると仮定すると、一致する行のないデータ ページを見つける可能性は 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 つだけですが、どれが最適かを判断するのは困難です。最初のアプローチでは、フィルタリングを行うために 2 番目のインデックス全体を読み取るコストがかかりますが、事前に並べ替えられたデータのマージ結合が許可される場合があります。2 番目と 3 番目では、データを事前にフィルター処理できますが、ハッシュ結合が必要です。ハッシュ結合にどれだけのコストがかかると思われるかを考えると、マージ結合が適切なオプションである可能性があります。

クエリには行ごとに 87 バイトのうち 52 バイトが必要であり、インデックスにはオーバーヘッドがあるため、テーブル自体よりも少ないスペースをインデックスが占めることにはならないかもしれません。

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

于 2012-12-05T00:08:20.637 に答える